├── .gitignore ├── .shellcheckrc ├── Dockerfile ├── LICENSE ├── Makefile ├── README.rst ├── Vagrantfile ├── debian ├── debootstrap │ ├── bullseye │ ├── buster │ ├── jessie │ ├── stretch │ ├── unstable │ └── wheezy ├── keys │ ├── buster.gpg │ ├── debian-archive-keyring.gpg │ └── unstable.gpg └── mkimage.sh ├── favicon.svg ├── recipes ├── java │ ├── corretto.sh │ ├── graalvm.sh │ ├── graalvm_slim.sh │ ├── gradle.sh │ ├── java.sh │ ├── java_slim.sh │ └── maven.sh ├── kafka │ └── kafka.sh ├── nodejs │ └── nodejs.sh ├── php │ └── php.sh └── python │ └── python.sh ├── scripts ├── cosign.py ├── gpg.py ├── security-scan.sh └── test.py └── test └── debian11-nodejs-23.11.0.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | debian/dist 2 | download 3 | *.tar.gz 4 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- 1 | disable=SC2068 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/official-images/debian 2 | 3 | RUN apt-get update && apt-get install curl debootstrap gpg make binutils unzip 4 | RUN apt-get install wget apt-transport-https gnupg lsb-release && 5 | && wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | apt-key add - 6 | && echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | tee -a /etc/apt/sources.list.d/trivy.list 7 | && apt-get update 8 | && apt-get install trivy -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Anton Krylov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Makefile Configuration Directives 3 | # ============================================================================== 4 | 5 | # Set 'help' as the default target when running just 'make' 6 | .DEFAULT_GOAL := help 7 | 8 | # Run all recipe lines in one shell (enables using shell variables across lines) 9 | .ONESHELL: 10 | 11 | # Automatically export all variables to recipe shells 12 | .EXPORT_ALL_VARIABLES: 13 | 14 | # Suppress command echoing unless VERBOSE=1 is set 15 | ifndef VERBOSE 16 | .SILENT: 17 | endif 18 | 19 | .DELETE_ON_ERROR: # Delete target files if recipe fails 20 | # ============================================================================== 21 | # Help 22 | # ============================================================================== 23 | help: 24 | @echo 25 | @echo "Usage: make " 26 | @echo 27 | @echo " help - Display this help message" 28 | @echo " all - Build all Debian images" 29 | @echo " check-dependencies - Verify required tools are installed" 30 | @echo " clean - Remove all build artifacts and downloads" 31 | @echo " list-vars - List all Makefile variables and their origins" 32 | @echo " shellcheck - Validate all bash scripts" 33 | @echo " package - Create tar.gz archive of the directory" 34 | @echo " release - Create Git tag and GitHub release" 35 | @echo " archive - Create git archive of HEAD" 36 | @echo " bundle - Create git bundle of repository" 37 | @echo " test - Run structure tests on built container images" 38 | @echo 39 | @echo " ============================" 40 | @echo " ** Debian Linux targets ** " 41 | @echo " ============================" 42 | @echo 43 | @echo "|all|" 44 | @echo 45 | @echo "|debian11|" 46 | @echo "|debian11-java|" 47 | @echo "|debian11-java-slim|" 48 | @echo "|debian11-corretto|" 49 | @echo "|debian11-graal|" 50 | @echo "|debian11-graal-slim|" 51 | @echo "|debian11-java-slim-maven|" 52 | @echo "|debian11-java-slim-gradle|" 53 | @echo "|debian11-graal-slim-maven|" 54 | @echo "|debian11-graal-slim-gradle|" 55 | @echo 56 | @echo "|debian11-java-kafka|" 57 | @echo "|debian11-java-slim-kafka|" 58 | @echo 59 | @echo "|debian11-nodejs-23.11.0|" 60 | @echo 61 | @echo "|debian11-python-3.9.18|" 62 | 63 | # ============================================================================== 64 | # Build Configuration 65 | # ============================================================================== 66 | 67 | SHELL := /bin/bash 68 | .SHELLFLAGS := -o pipefail -ec 69 | 70 | THIS_FILE := $(lastword $(MAKEFILE_LIST)) 71 | SRCDIR := $(abspath $(patsubst %/,%,$(dir $(THIS_FILE)))) 72 | DOWNLOAD_DIR := $(SRCDIR)/download 73 | SCRIPTS_DIR := $(SRCDIR)/scripts 74 | 75 | DEBIAN_DIR := $(SRCDIR)/debian 76 | DEBIAN_BUILD_SCRIPT := $(DEBIAN_DIR)/mkimage.sh 77 | DEBIAN_KEYS_DIR := $(DEBIAN_DIR)/keys 78 | DEBIAN_KEYRING := $(DEBIAN_KEYS_DIR)/debian-archive-keyring.gpg 79 | 80 | # Validate keyring exists 81 | ifeq (,$(wildcard $(DEBIAN_KEYRING))) 82 | $(error Debian keyring not found at $(DEBIAN_KEYRING)) 83 | endif 84 | 85 | # Build options 86 | VARIANT ?= container 87 | RELEASE ?= stable 88 | 89 | # Validate VARIANT 90 | VALID_VARIANTS := container fakechroot minbase 91 | ifneq (,$(filter-out $(VALID_VARIANTS),$(VARIANT))) 92 | $(error Invalid VARIANT '$(VARIANT)'. Must be one of: $(VALID_VARIANTS)) 93 | endif 94 | 95 | # Version information 96 | VERSION := $(shell git describe --tags 2>/dev/null || echo "0.1.0") 97 | BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") 98 | GIT_REVISION := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") 99 | 100 | # Export for child processes 101 | export VERSION BUILD_DATE GIT_REVISION 102 | 103 | COLOR_RESET := \033[0m 104 | COLOR_GREEN := \033[32m 105 | COLOR_YELLOW := \033[33m 106 | 107 | # Print header with timestamp and color 108 | PRINT_HEADER = @printf "$(COLOR_GREEN)\n[%s] %-60s$(COLOR_RESET)\n" "$$(date +'%Y-%m-%d %H:%M:%S')" "Building: $@" 109 | 110 | # Recipes 111 | SCRIPTS := $(SRCDIR)/scripts/ 112 | RECIPES_DIR := $(SRCDIR)/recipes 113 | JAVA_RECIPES := $(RECIPES_DIR)/java/ 114 | PYTHON_RECIPES := $(RECIPES_DIR)/python/ 115 | NODEJS_RECIPES := $(RECIPES_DIR)/nodejs/ 116 | 117 | # ============================================================================== 118 | # Build Targets 119 | # ============================================================================== 120 | 121 | .PHONY: debian11 debian11-java debian11-java-slim debian11-graal \ 122 | debian11-graal-slim debian11-corretto debian11-java-slim-maven \ 123 | debian11-java-slim-gradle debian11-nodejs-23.11.0 debian11-java-slim-kafka \ 124 | debian11-java-kafka debian11-python-3.9.18 125 | 126 | .PHONY: all 127 | all: debian11 debian11-java debian11-java-slim debian11-graal \ 128 | debian11-graal-slim debian11-corretto debian11-java-slim-maven \ 129 | debian11-java-slim-gradle debian11-nodejs-23.11.0 debian11-java-slim-kafka \ 130 | debian11-java-kafka debian11-python-3.9.18 131 | 132 | debian11: 133 | $(PRINT_HEADER) 134 | $(DEBIAN_BUILD_SCRIPT) \ 135 | --name=$@ \ 136 | --keyring=$(DEBIAN_KEYRING) \ 137 | --variant=$(VARIANT) \ 138 | --release=$(RELEASE) \ 139 | --scripts=$(SCRIPTS)/security-scan.sh 140 | 141 | debian11-java: 142 | $(PRINT_HEADER) 143 | $(DEBIAN_BUILD_SCRIPT) \ 144 | --name=$@ \ 145 | --keyring=$(DEBIAN_KEYRING) \ 146 | --variant=$(VARIANT) \ 147 | --release=$(RELEASE) \ 148 | --recipes=$(JAVA_RECIPES)/java.sh \ 149 | --scripts=$(SCRIPTS)/security-scan.sh 150 | 151 | debian11-java-slim: 152 | $(PRINT_HEADER) 153 | $(DEBIAN_BUILD_SCRIPT) \ 154 | --name=$@ \ 155 | --keyring=$(DEBIAN_KEYRING) \ 156 | --variant=$(VARIANT) \ 157 | --release=$(RELEASE) \ 158 | --recipes=$(JAVA_RECIPES)/java_slim.sh \ 159 | --scripts=$(SCRIPTS)/security-scan.sh 160 | 161 | debian11-graal: 162 | $(PRINT_HEADER) 163 | $(DEBIAN_BUILD_SCRIPT) \ 164 | --name=$@ \ 165 | --keyring=$(DEBIAN_KEYRING) \ 166 | --variant=$(VARIANT) \ 167 | --release=$(RELEASE) \ 168 | --recipes=$(JAVA_RECIPES)/graalvm.sh \ 169 | --scripts=$(SCRIPTS)/security-scan.sh 170 | 171 | debian11-graal-slim: 172 | $(PRINT_HEADER) 173 | $(DEBIAN_BUILD_SCRIPT) \ 174 | --name=$@ \ 175 | --keyring=$(DEBIAN_KEYRING) \ 176 | --variant=$(VARIANT) \ 177 | --release=$(RELEASE) \ 178 | --recipes=$(JAVA_RECIPES)/graalvm_slim.sh \ 179 | --scripts=$(SCRIPTS)/security-scan.sh 180 | 181 | debian11-corretto: 182 | $(PRINT_HEADER) 183 | $(DEBIAN_BUILD_SCRIPT) \ 184 | --name=$@ \ 185 | --keyring=$(DEBIAN_KEYRING) \ 186 | --variant=$(VARIANT) \ 187 | --release=$(RELEASE) \ 188 | --recipes=$(JAVA_RECIPES)/corretto.sh \ 189 | --scripts=$(SCRIPTS)/security-scan.sh 190 | 191 | debian11-java-slim-maven: 192 | $(PRINT_HEADER) 193 | $(DEBIAN_BUILD_SCRIPT) \ 194 | --name=$@ \ 195 | --keyring=$(DEBIAN_KEYRING) \ 196 | --variant=$(VARIANT) \ 197 | --release=$(RELEASE) \ 198 | --recipes=$(JAVA_RECIPES)/java_slim.sh,$(JAVA_RECIPES)/maven.sh \ 199 | --scripts=$(SCRIPTS)/security-scan.sh 200 | 201 | debian11-java-slim-gradle: 202 | $(PRINT_HEADER) 203 | $(DEBIAN_BUILD_SCRIPT) \ 204 | --name=$@ \ 205 | --keyring=$(DEBIAN_KEYRING) \ 206 | --variant=$(VARIANT) \ 207 | --release=$(RELEASE) \ 208 | --recipes=$(JAVA_RECIPES)/java_slim.sh,$(JAVA_RECIPES)/gradle.sh \ 209 | --scripts=$(SCRIPTS)/security-scan.sh 210 | 211 | debian11-graal-slim-maven: 212 | $(PRINT_HEADER) 213 | $(DEBIAN_BUILD_SCRIPT) \ 214 | --name=$@ \ 215 | --keyring=$(DEBIAN_KEYRING) \ 216 | --variant=$(VARIANT) \ 217 | --release=$(RELEASE) \ 218 | --recipes=$(JAVA_RECIPES)/graalvm_slim.sh,$(JAVA_RECIPES)/maven.sh \ 219 | --scripts=$(SCRIPTS)/security-scan.sh 220 | 221 | debian11-graal-slim-gradle: 222 | $(PRINT_HEADER) 223 | $(DEBIAN_BUILD_SCRIPT) \ 224 | --name=$@ \ 225 | --keyring=$(DEBIAN_KEYRING) \ 226 | --variant=$(VARIANT) \ 227 | --release=$(RELEASE) \ 228 | --recipes=$(JAVA_RECIPES)/graalvm_slim.sh,$(JAVA_RECIPES)/gradle.sh \ 229 | --scripts=$(SCRIPTS)/security-scan.sh 230 | 231 | 232 | debian11-java-kafka: 233 | $(PRINT_HEADER) 234 | $(DEBIAN_BUILD_SCRIPT) \ 235 | --name=$@ \ 236 | --keyring=$(DEBIAN_KEYRING) \ 237 | --variant=$(VARIANT) \ 238 | --release=$(RELEASE) \ 239 | --recipes=$(JAVA_RECIPES)/java.sh,$(RECIPES)/kafka/kafka.sh \ 240 | --scripts=$(SCRIPTS)/security-scan.sh 241 | 242 | debian11-java-slim-kafka: 243 | $(PRINT_HEADER) 244 | $(DEBIAN_BUILD_SCRIPT) \ 245 | --name=$@ \ 246 | --keyring=$(DEBIAN_KEYRING) \ 247 | --variant=$(VARIANT) \ 248 | --release=$(RELEASE) \ 249 | --recipes=$(JAVA_RECIPES)/java_slim.sh,$(RECIPES)/kafka/kafka.sh \ 250 | --scripts=$(SCRIPTS)/security-scan.sh 251 | 252 | 253 | debian11-nodejs-23.11.0: 254 | $(PRINT_HEADER) 255 | $(DEBIAN_BUILD_SCRIPT) \ 256 | --name=$@ \ 257 | --keyring=$(DEBIAN_KEYRING) \ 258 | --variant=$(VARIANT) \ 259 | --release=$(RELEASE) \ 260 | --recipes=$(NODEJS_RECIPES)/nodejs.sh \ 261 | --scripts=$(SCRIPTS)/security-scan.sh 262 | 263 | debian11-python-3.9.18: 264 | $(PRINT_HEADER) 265 | $(DEBIAN_BUILD_SCRIPT) \ 266 | --name=$@ \ 267 | --keyring=$(DEBIAN_KEYRING) \ 268 | --variant=$(VARIANT) \ 269 | --release=$(RELEASE) \ 270 | --recipes=$(PYTHON_RECIPES)/python.sh \ 271 | --scripts=$(SCRIPTS)/security-scan.sh 272 | 273 | debian11-php-8.2.12: 274 | $(PRINT_HEADER) 275 | $(DEBIAN_BUILD_SCRIPT) \ 276 | --name=$@ \ 277 | --keyring=$(DEBIAN_KEYRING) \ 278 | --variant=$(VARIANT) \ 279 | --release=$(RELEASE) \ 280 | --recipes=$(RECIPES_DIR)/php/php.sh \ 281 | --scripts=$(SCRIPTS)/security-scan.sh 282 | 283 | 284 | # ============================================================================== 285 | # Test Targets 286 | # ============================================================================== 287 | 288 | TEST_CONFIG_DIR := $(SRCDIR)/test 289 | CONTAINER_TEST_SCRIPT := $(SCRIPTS_DIR)/test.py 290 | 291 | .PHONY: test 292 | test: ## Run structure tests on built container images 293 | $(PRINT_HEADER) 294 | @echo "Running structure tests on container images..." 295 | @for image in $(DIST_DIR)/*; do \ 296 | if [ -d "$$image" ]; then \ 297 | image_name=$$(basename $$image); \ 298 | config_file=$(TEST_CONFIG_DIR)/$$image_name.yaml; \ 299 | if [ -f "$$config_file" ]; then \ 300 | echo "Testing image: $$image_name with config: $$config_file"; \ 301 | $(CONTAINER_TEST_SCRIPT) --image $$image_name --config $$config_file; \ 302 | else \ 303 | echo "No test config found for image: $$image_name"; \ 304 | fi; \ 305 | fi; \ 306 | done 307 | @echo "All tests completed." 308 | 309 | # ============================================================================== 310 | # Utility Targets 311 | # ============================================================================== 312 | REQUIRED_TOOLS := docker bash grep sed awk debootstrap unzip 313 | check-dependencies: 314 | $(PRINT_HEADER) 315 | @echo "Checking required dependencies..." 316 | @for tool in $(REQUIRED_TOOLS); do \ 317 | if ! command -v $$tool >/dev/null 2>&1; then \ 318 | echo "Error: Required tool '$$tool' is not installed."; \ 319 | exit 1; \ 320 | else \ 321 | echo "Found: $$tool"; \ 322 | fi; \ 323 | done 324 | @echo "All dependencies are satisfied." 325 | 326 | 327 | DOWNLOADS_DIR := download 328 | DIST_DIR := debian/dist 329 | 330 | clean: ## Remove all build artifacts and downloads 331 | @echo -e "Cleaning build artifacts and downloads..." 332 | @if [ -d "$(DIST_DIR)" ]; then \ 333 | echo -e "Removing distributions..."; \ 334 | rm -rf $(DIST_DIR)/*; \ 335 | fi 336 | @if [ -d "$(DOWNLOADS_DIR)" ]; then \ 337 | echo -e "Removing downloaded files..."; \ 338 | rm -rf $(DOWNLOADS_DIR)/*; \ 339 | fi 340 | 341 | .PHONY: list-vars 342 | list-vars: 343 | @echo "Variable Name Origin" 344 | @echo "-------------------- -----------" 345 | @$(foreach var, $(filter-out .% %_FILES, $(.VARIABLES)), \ 346 | $(if $(filter-out default automatic, $(origin $(var))), \ 347 | printf "%-20s %s\\n" "$(var)" "$(origin $(var))"; \ 348 | )) 349 | 350 | GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) 351 | GIT_COMMIT := $(shell git rev-parse --short HEAD) 352 | 353 | .PHONY: package 354 | package: 355 | @echo "Creating a tar.gz archive of the entire directory..." 356 | @DIR_NAME=$$(basename $$(pwd)); \ 357 | TAR_FILE="$$DIR_NAME.tar.gz"; \ 358 | tar -czvf $$TAR_FILE .; \ 359 | echo "Archive created successfully: $$TAR_FILE" 360 | 361 | .PHONY: release 362 | release: 363 | @echo "Creating Git tag and releasing on GitHub..." 364 | @read -p "Enter the version number (e.g., v1.0.0): " version; \ 365 | git tag -a $$version -m "Release $$version"; \ 366 | git push origin $$version; \ 367 | gh release create $$version --generate-notes 368 | @echo "Release $$version created and pushed to GitHub." 369 | 370 | .PHONY: archive 371 | archive: 372 | @echo "Creating git archive..." 373 | git archive --format=tar.gz --output=archive-$(GIT_BRANCH)-$(GIT_COMMIT).tar.gz HEAD 374 | @echo "Archive created: archive-$(GIT_BRANCH)-$(GIT_COMMIT).tar.gz" 375 | 376 | .PHONY: bundle 377 | bundle: 378 | @echo "Creating git bundle..." 379 | git bundle create bundle-$(GIT_BRANCH)-$(GIT_COMMIT).bundle --all 380 | @echo "Bundle created: bundle-$(GIT_BRANCH)-$(GIT_COMMIT).bundle" 381 | 382 | .PHONY: shellcheck 383 | shellcheck: 384 | @shellcheck --severity=error --enable=all --shell=bash $(shell find . -type f -name "*.sh") 385 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Container Tools 2 | ============= 3 | 4 | .. image:: https://raw.githubusercontent.com/avkcode/container-tools/refs/heads/main/favicon.svg 5 | :alt: Container Tools Logo 6 | :width: 80px 7 | :align: right 8 | 9 | Container Tools provides scripts and utilities to automate the creation of minimal Debian-based root filesystems (rootfs) using debootstrap. It supports customization with specific packages, configurations, and integrates security scanning for containerized environments. Easily extensible for other distros and projects. 10 | 11 | Rationale 12 | -------- 13 | 14 | Traditional Dockerfile-based builds suffer from several inefficiencies: 15 | 16 | - **Storage bloat**: Each ``RUN apt-get install`` creates a new layer, wasting disk space with duplicate dependencies 17 | - **Network inefficiency**: Redundant package downloads across different images 18 | - **Slow iterations**: Rebuilding images requires repeating all previous steps 19 | 20 | This tool enables you to: 21 | 22 | - Build minimal base images from scratch using debootstrap 23 | - Precisely include only required components in the initial build 24 | - Create specialized variants (Java, Kafka, etc.) from common foundations 25 | 26 | Features 27 | -------- 28 | 29 | - Lightweight Debian-based rootfs generation 30 | - Customizable package selection 31 | - Security scanning integration (Trivy) 32 | - Support for Java variants (Standard, GraalVM, Corretto) 33 | - Build tool integration (Maven, Gradle) 34 | - Clean room build via Firecracker sandbox 35 | 36 | Quick Start 37 | ----------- 38 | 39 | Prerequisites 40 | ~~~~~~~~~~~~~ 41 | 42 | - Linux system (or VM) 43 | - Docker 44 | - debootstrap 45 | - make 46 | - curl, unzip, sudo 47 | 48 | Displaying Help 49 | -------------- 50 | 51 | To view all available build targets and their descriptions, run: 52 | 53 | .. code-block:: bash 54 | 55 | make help 56 | 57 | This will display: 58 | - All available image build targets (Debian, Java, GraalVM, etc.) 59 | - Utility targets (clean, test, shellcheck) 60 | - Dependency checking commands 61 | 62 | For detailed information about a specific target, you can also view the Makefile directly. 63 | 64 | Building Images 65 | ~~~~~~~~~~~~~~ 66 | 67 | .. code-block:: bash 68 | 69 | git clone https://github.com/avkcode/container-tools.git 70 | cd container-tools 71 | make debian11-java-slim # Example target 72 | 73 | Available targets: 74 | 75 | :: 76 | 77 | debian11 78 | debian11-java 79 | debian11-java-slim 80 | debian11-corretto 81 | debian11-graal 82 | debian11-graal-slim 83 | debian11-java-slim-maven 84 | debian11-java-slim-gradle 85 | debian11-graal-slim-maven 86 | debian11-graal-slim-gradle 87 | debian11-java-kafka 88 | debian11-java-slim-kafka 89 | debian11-nodejs 90 | 91 | Using Built Images 92 | ~~~~~~~~~~~~~~~~~ 93 | 94 | After successful build: 95 | 96 | .. code-block:: bash 97 | 98 | # Load the image 99 | cat debian/dist/debian11-graal-slim/debian11-graal-slim.tar | docker import - debian11-graal-slim 100 | 101 | # Run the container 102 | docker run -it debian11-graal-slim /bin/bash 103 | 104 | Extending the Tool 105 | ----------------- 106 | 107 | To add new components: 108 | 109 | 1. Create a recipe in ``recipes/`` directory 110 | 2. Verify artifact URLs and SHA256 checksums 111 | 3. Add a new target to the Makefile 112 | 113 | Clean Room Building with Firecracker 114 | ----------------------------------- 115 | 116 | For secure, isolated builds: 117 | 118 | 1. Set up Firecracker sandbox: 119 | 120 | Visit the Firecracker sandbox repository at https://github.com/avkcode/firecracker-sandbox. 121 | 122 | Firecracker requires bootable rootfs image and Linux Kernel. To create rootfs and download prebuilt Kernel execute ``create-debian-rootfs.sh`` script: 123 | 124 | .. code-block:: bash 125 | 126 | git clone https://github.com/avkcode/firecracker-sandbox.git 127 | cd firecracker-sandbox 128 | bash tools/create-debian-rootfs.sh 129 | 130 | It should produce ``firecracker-rootfs.ext4`` and ``vmlinux`` files. ``vm-config.json`` is used for VM boot options. 131 | If you want to compile custom Kernel use ``tools\download-and-build-kernel.sh`` script. 132 | 133 | 2. Configure networking: 134 | 135 | .. code-block:: bash 136 | 137 | make net-up 138 | make activate 139 | make up 140 | 141 | 3. Install dependencies in the VM: 142 | 143 | .. code-block:: bash 144 | 145 | apt-get install docker.io git make debootstrap sudo unzip curl 146 | 147 | 4. Build your images as usual 148 | 149 | Repository Structure 150 | ------------------- 151 | 152 | :: 153 | 154 | container-tools/ 155 | ├── Dockerfile # Docker environment configuration 156 | ├── Makefile # Build automation 157 | ├── debian/ 158 | │ ├── debootstrap/ # Debian version configs 159 | │ ├── keys/ # GPG keys for verification 160 | │ └── mkimage.sh # Rootfs builder script 161 | ├── recipes/ 162 | │ ├── java/ # Java variants 163 | │ └── kafka/ # Kafka installation 164 | ├── scripts/ # Maintenance scripts 165 | ├── dist/ # Output images 166 | └── download/ # Temporary downloads 167 | 168 | GPG 169 | --- 170 | 171 | Sign .tar Files 172 | To sign .tar files, provide the directory or file path along with your GPG key ID: 173 | 174 | .. code-block:: bash 175 | 176 | ./scripts/gpg.py --directory /path/to/tar/files --gpg-key-id YOUR_KEY_ID 177 | 178 | The script generates an ASCII-armored signature file (.asc) for each .tar file. 179 | If a signature file already exists, the script prompts to overwrite it. 180 | 181 | Verify .tar Files 182 | To verify .tar files, use the --verify flag: 183 | 184 | .. code-block:: bash 185 | 186 | ./scripts/gpg.py --directory /path/to/tar/files --verify 187 | 188 | By default, the script looks for a .asc signature file with the same name as the .tar file. 189 | To specify a custom signature file, use the --sig-file option: 190 | 191 | .. code-block:: bash 192 | 193 | ./scripts/gpg.py --directory /path/to/file.tar --verify --sig-file /path/to/signature.asc 194 | 195 | 196 | Cosign 197 | ------ 198 | 199 | Sign .tar files in a specific directory: 200 | 201 | .. code-block:: bash 202 | 203 | ./cosign.py --directory=path/to/tar/files 204 | 205 | Use a Private Key for Signing 206 | Sign images using the private key generated earlier: 207 | 208 | .. code-block:: bash 209 | 210 | ./cosign.py --directory=path/to/tar/files --key=cosign.key 211 | 212 | Push Signed Images to a Registry 213 | Push signed images to a container registry: 214 | 215 | .. code-block:: bash 216 | 217 | ./cosign.py --directory=path/to/tar/files --registry=myregistry.com/myrepo 218 | 219 | Perform a Dry Run 220 | Simulate the signing process without executing commands: 221 | 222 | .. code-block:: bash 223 | 224 | ./cosign.py --directory=path/to/tar/files --dry-run 225 | 226 | Step 5: Verify the Signatures 227 | 228 | After signing, you can verify the signatures using cosign: 229 | 230 | .. code-block:: bash 231 | 232 | cosign verify --key cosign.pub 233 | 234 | Test 235 | ---- 236 | 237 | Container-structure-test is a CLI tool for validating container images. 238 | It ensures images meet configuration, security, and compliance standards by running tests against file structures, 239 | metadata, environment variables, and commands within the image. 240 | Ideal for CI/CD pipelines, it helps catch issues early and ensures consistent, reliable container builds. 241 | 242 | Install container-structure-test: 243 | 244 | .. code-block:: bash 245 | 246 | curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 247 | chmod +x container-structure-test-linux-amd64 248 | sudo mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test 249 | 250 | Test a single image with a specific config: 251 | 252 | .. code-block:: bash 253 | 254 | ./scripts/container_test.py --image --config test/debian11-nodejs-23.11.0.yaml 255 | 256 | 257 | Security 258 | -------- 259 | 260 | All builds include automated security scanning via Trivy in the ``security-scan.sh`` script. 261 | 262 | Contributing 263 | ------------ 264 | 265 | Contributions are welcome. Please submit issues or pull requests for: 266 | 267 | - New distro support 268 | - Additional package recipes 269 | - Security improvements 270 | - Documentation enhancements 271 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | LINUX_BOX = "spox/ubuntu-arm" 2 | CPUS = "2" 3 | 4 | Vagrant.configure(2) do |config| 5 | config.vm.define "containerImages", autostart: true, primary: true do |config| 6 | config.vm.box = LINUX_BOX 7 | config.vm.hostname = "containerImages" 8 | config = configureProviders config, 9 | cpus: CPUS 10 | config.vm.synced_folder '../','/opt/containerImages' 11 | end 12 | 13 | def configureProviders(config, cpus: "2", memory: "2048") 14 | config.vm.provider "vmware_desktop" do |v| 15 | v.memory = memory 16 | v.cpus = cpus 17 | v.ssh_info_public = true 18 | v.gui = true 19 | end 20 | return config 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /debian/debootstrap/bullseye: -------------------------------------------------------------------------------- 1 | mirror_style release 2 | download_style apt 3 | finddebs_style from-indices 4 | variants - container fakechroot 5 | keyring /usr/share/keyrings/debian-archive-keyring.gpg 6 | 7 | if doing_variant fakechroot; then 8 | test "$FAKECHROOT" = "true" || error 1 FAKECHROOTREQ "This variant requires fakechroot environment to be started" 9 | fi 10 | 11 | case $ARCH in 12 | alpha|ia64) LIBC="libc6.1" ;; 13 | kfreebsd-*) LIBC="libc0.1" ;; 14 | hurd-*) LIBC="libc0.3" ;; 15 | *) LIBC="libc6" ;; 16 | esac 17 | 18 | work_out_debs () { 19 | # adduser in case users want to add a user to run as non-root 20 | # base-files as it has many important files 21 | # base-passwd to get user account info 22 | # bash because users will often shell in 23 | # bsdutils because it has some commands used in postinst 24 | # - particularly `logger` for `mysql-server` see 25 | # https://github.com/bitnami/minideb/issues/16 26 | # coreutils for many very common utilities 27 | # dash for a shell for scripts 28 | # debian-archive-keyring to verify apt packages 29 | # diffutils for diff as required for installing the system 30 | # (could maybe be removed after, but diffing is pretty common in debugging) 31 | # dpkg for dpkg 32 | # findutils for find as required for installing the system 33 | # grep as it is a very common debugging tool 34 | # gzip as decompressing zip is super common 35 | # hostname ? 36 | # libc-bin for ldconfig 37 | # login as su maybe used if run as non root (?) 38 | # lsb-base ? 39 | # mawk as it is used by dpkg 40 | # ncurses-base for terminfo files as docker sets TERM=xterm 41 | # see https://github.com/bitnami/minideb/issues/17 42 | # passwd for managing user accounts if run as non-root. 43 | # sed as a very commonly used tool 44 | # sysv-rc for update-rc.d, required when installing initscripts in postinsts 45 | # tar as uncompressing tarballs is super common when installing things. 46 | # tzdata for handling timezones 47 | # util-linux for getopt 48 | # mount is required for mounting /proc during debootstrap 49 | required="adduser base-files base-passwd bash bsdutils coreutils dash debian-archive-keyring diffutils dpkg findutils grep gzip hostname init-system-helpers libc-bin login lsb-base mawk ncurses-base passwd sed sysv-rc tar tzdata util-linux mount" 50 | 51 | base="apt" 52 | 53 | if doing_variant fakechroot; then 54 | # ldd.fake needs binutils 55 | required="$required binutils" 56 | fi 57 | 58 | case $MIRRORS in 59 | https://*) 60 | base="$base apt-transport-https ca-certificates" 61 | ;; 62 | esac 63 | } 64 | 65 | first_stage_install () { 66 | extract $required 67 | 68 | mkdir -p "$TARGET/var/lib/dpkg" 69 | : >"$TARGET/var/lib/dpkg/status" 70 | : >"$TARGET/var/lib/dpkg/available" 71 | 72 | setup_etc 73 | if [ ! -e "$TARGET/etc/fstab" ]; then 74 | echo '# UNCONFIGURED FSTAB FOR BASE SYSTEM' > "$TARGET/etc/fstab" 75 | chown 0:0 "$TARGET/etc/fstab"; chmod 644 "$TARGET/etc/fstab" 76 | fi 77 | 78 | setup_devices 79 | 80 | x_feign_install () { 81 | local pkg="$1" 82 | local deb="$(debfor $pkg)" 83 | local ver="$(extract_deb_field "$TARGET/$deb" Version)" 84 | 85 | mkdir -p "$TARGET/var/lib/dpkg/info" 86 | 87 | echo \ 88 | "Package: $pkg 89 | Version: $ver 90 | Maintainer: unknown 91 | Status: install ok installed" >> "$TARGET/var/lib/dpkg/status" 92 | 93 | touch "$TARGET/var/lib/dpkg/info/${pkg}.list" 94 | } 95 | 96 | x_feign_install dpkg 97 | } 98 | 99 | second_stage_install () { 100 | setup_dynamic_devices 101 | 102 | x_core_install () { 103 | smallyes '' | in_target dpkg --force-depends --install $(debfor "$@") 104 | } 105 | 106 | p () { 107 | baseprog="$(($baseprog + ${1:-1}))" 108 | } 109 | 110 | if doing_variant fakechroot; then 111 | setup_proc_fakechroot 112 | else 113 | setup_proc 114 | in_target /sbin/ldconfig 115 | fi 116 | 117 | DEBIAN_FRONTEND=noninteractive 118 | DEBCONF_NONINTERACTIVE_SEEN=true 119 | export DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN 120 | 121 | baseprog=0 122 | bases=7 123 | 124 | p; progress $baseprog $bases INSTCORE "Installing core packages" #1 125 | info INSTCORE "Installing core packages..." 126 | 127 | p; progress $baseprog $bases INSTCORE "Installing core packages" #2 128 | ln -sf mawk "$TARGET/usr/bin/awk" 129 | x_core_install base-passwd 130 | x_core_install base-files 131 | p; progress $baseprog $bases INSTCORE "Installing core packages" #3 132 | x_core_install dpkg 133 | 134 | if [ ! -e "$TARGET/etc/localtime" ]; then 135 | ln -sf /usr/share/zoneinfo/UTC "$TARGET/etc/localtime" 136 | fi 137 | 138 | if doing_variant fakechroot; then 139 | install_fakechroot_tools 140 | fi 141 | 142 | p; progress $baseprog $bases INSTCORE "Installing core packages" #4 143 | x_core_install $LIBC 144 | 145 | p; progress $baseprog $bases INSTCORE "Installing core packages" #5 146 | x_core_install perl-base 147 | 148 | p; progress $baseprog $bases INSTCORE "Installing core packages" #6 149 | rm "$TARGET/usr/bin/awk" 150 | x_core_install mawk 151 | 152 | p; progress $baseprog $bases INSTCORE "Installing core packages" #7 153 | if doing_variant -; then 154 | x_core_install debconf 155 | fi 156 | 157 | baseprog=0 158 | bases=$(set -- $required; echo $#) 159 | 160 | info UNPACKREQ "Unpacking required packages..." 161 | 162 | exec 7>&1 163 | 164 | smallyes '' | 165 | (repeatn 5 in_target_failmsg UNPACK_REQ_FAIL_FIVE "Failure while unpacking required packages. This will be attempted up to five times." "" \ 166 | dpkg --status-fd 8 --force-depends --unpack $(debfor $required) 8>&1 1>&7 || echo EXITCODE $?) | 167 | dpkg_progress $baseprog $bases UNPACKREQ "Unpacking required packages" UNPACKING 168 | 169 | info CONFREQ "Configuring required packages..." 170 | 171 | echo \ 172 | "#!/bin/sh 173 | exit 101" > "$TARGET/usr/sbin/policy-rc.d" 174 | chmod 755 "$TARGET/usr/sbin/policy-rc.d" 175 | 176 | mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL" 177 | echo \ 178 | "#!/bin/sh 179 | echo 180 | echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon" 181 | chmod 755 "$TARGET/sbin/start-stop-daemon" 182 | 183 | setup_dselect_method apt 184 | 185 | smallyes '' | 186 | (in_target_failmsg CONF_REQ_FAIL "Failure while configuring required packages." "" \ 187 | dpkg --status-fd 8 --configure --pending --force-configure-any --force-depends 8>&1 1>&7 || echo EXITCODE $?) | 188 | dpkg_progress $baseprog $bases CONFREQ "Configuring required packages" CONFIGURING 189 | 190 | baseprog=0 191 | bases="$(set -- $base; echo $#)" 192 | 193 | info UNPACKBASE "Unpacking the base system..." 194 | 195 | setup_available $required $base 196 | done_predeps= 197 | while predep=$(get_next_predep); do 198 | # We have to resolve dependencies of pre-dependencies manually because 199 | # dpkg --predep-package doesn't handle this. 200 | predep=$(without "$(without "$(resolve_deps $predep)" "$required")" "$done_predeps") 201 | # XXX: progress is tricky due to how dpkg_progress works 202 | # -- cjwatson 2009-07-29 203 | p; smallyes '' | 204 | in_target dpkg --force-overwrite --force-confold --skip-same-version --install $(debfor $predep) 205 | base=$(without "$base" "$predep") 206 | done_predeps="$done_predeps $predep" 207 | done 208 | 209 | smallyes '' | 210 | (repeatn 5 in_target_failmsg INST_BASE_FAIL_FIVE "Failure while installing base packages. This will be re-attempted up to five times." "" \ 211 | dpkg --status-fd 8 --force-overwrite --force-confold --skip-same-version --unpack $(debfor $base) 8>&1 1>&7 || echo EXITCODE $?) | 212 | dpkg_progress $baseprog $bases UNPACKBASE "Unpacking base system" UNPACKING 213 | 214 | info CONFBASE "Configuring the base system..." 215 | 216 | smallyes '' | 217 | (repeatn 5 in_target_failmsg CONF_BASE_FAIL_FIVE "Failure while configuring base packages. This will be re-attempted up to five times." "" \ 218 | dpkg --status-fd 8 --force-confold --skip-same-version --configure -a 8>&1 1>&7 || echo EXITCODE $?) | 219 | dpkg_progress $baseprog $bases CONFBASE "Configuring base system" CONFIGURING 220 | 221 | mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon" 222 | rm -f "$TARGET/usr/sbin/policy-rc.d" 223 | 224 | progress $bases $bases CONFBASE "Configuring base system" 225 | info BASESUCCESS "Base system installed successfully." 226 | } 227 | -------------------------------------------------------------------------------- /debian/debootstrap/buster: -------------------------------------------------------------------------------- 1 | mirror_style release 2 | download_style apt 3 | finddebs_style from-indices 4 | variants - container fakechroot 5 | keyring /usr/share/keyrings/debian-archive-keyring.gpg 6 | 7 | if doing_variant fakechroot; then 8 | test "$FAKECHROOT" = "true" || error 1 FAKECHROOTREQ "This variant requires fakechroot environment to be started" 9 | fi 10 | 11 | case $ARCH in 12 | alpha|ia64) LIBC="libc6.1" ;; 13 | kfreebsd-*) LIBC="libc0.1" ;; 14 | hurd-*) LIBC="libc0.3" ;; 15 | *) LIBC="libc6" ;; 16 | esac 17 | 18 | work_out_debs () { 19 | # adduser in case users want to add a user to run as non-root 20 | # base-files as it has many important files 21 | # base-passwd to get user account info 22 | # bash because users will often shell in 23 | # bsdutils because it has some commands used in postinst 24 | # - particularly `logger` for `mysql-server` see 25 | # https://github.com/bitnami/minideb/issues/16 26 | # coreutils for many very common utilities 27 | # dash for a shell for scripts 28 | # debian-archive-keyring to verify apt packages 29 | # diffutils for diff as required for installing the system 30 | # (could maybe be removed after, but diffing is pretty common in debugging) 31 | # dpkg for dpkg 32 | # findutils for find as required for installing the system 33 | # grep as it is a very common debugging tool 34 | # gzip as decompressing zip is super common 35 | # hostname ? 36 | # libc-bin for ldconfig 37 | # login as su maybe used if run as non root (?) 38 | # lsb-base ? 39 | # mawk as it is used by dpkg 40 | # ncurses-base for terminfo files as docker sets TERM=xterm 41 | # see https://github.com/bitnami/minideb/issues/17 42 | # passwd for managing user accounts if run as non-root. 43 | # sed as a very commonly used tool 44 | # sysv-rc for update-rc.d, required when installing initscripts in postinsts 45 | # tar as uncompressing tarballs is super common when installing things. 46 | # tzdata for handling timezones 47 | # util-linux for getopt 48 | # mount is required for mounting /proc during debootstrap 49 | required="adduser base-files base-passwd bash bsdutils coreutils dash debian-archive-keyring diffutils dpkg findutils grep gzip hostname init-system-helpers libc-bin login lsb-base mawk ncurses-base passwd sed sysv-rc tar tzdata util-linux mount" 50 | 51 | base="apt" 52 | 53 | if doing_variant fakechroot; then 54 | # ldd.fake needs binutils 55 | required="$required binutils" 56 | fi 57 | 58 | case $MIRRORS in 59 | https://*) 60 | base="$base apt-transport-https ca-certificates" 61 | ;; 62 | esac 63 | } 64 | 65 | first_stage_install () { 66 | extract $required 67 | 68 | mkdir -p "$TARGET/var/lib/dpkg" 69 | : >"$TARGET/var/lib/dpkg/status" 70 | : >"$TARGET/var/lib/dpkg/available" 71 | 72 | setup_etc 73 | if [ ! -e "$TARGET/etc/fstab" ]; then 74 | echo '# UNCONFIGURED FSTAB FOR BASE SYSTEM' > "$TARGET/etc/fstab" 75 | chown 0:0 "$TARGET/etc/fstab"; chmod 644 "$TARGET/etc/fstab" 76 | fi 77 | 78 | setup_devices 79 | 80 | x_feign_install () { 81 | local pkg="$1" 82 | local deb="$(debfor $pkg)" 83 | local ver="$(extract_deb_field "$TARGET/$deb" Version)" 84 | 85 | mkdir -p "$TARGET/var/lib/dpkg/info" 86 | 87 | echo \ 88 | "Package: $pkg 89 | Version: $ver 90 | Maintainer: unknown 91 | Status: install ok installed" >> "$TARGET/var/lib/dpkg/status" 92 | 93 | touch "$TARGET/var/lib/dpkg/info/${pkg}.list" 94 | } 95 | 96 | x_feign_install dpkg 97 | } 98 | 99 | second_stage_install () { 100 | setup_dynamic_devices 101 | 102 | x_core_install () { 103 | smallyes '' | in_target dpkg --force-depends --install $(debfor "$@") 104 | } 105 | 106 | p () { 107 | baseprog="$(($baseprog + ${1:-1}))" 108 | } 109 | 110 | if doing_variant fakechroot; then 111 | setup_proc_fakechroot 112 | else 113 | setup_proc 114 | in_target /sbin/ldconfig 115 | fi 116 | 117 | DEBIAN_FRONTEND=noninteractive 118 | DEBCONF_NONINTERACTIVE_SEEN=true 119 | export DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN 120 | 121 | baseprog=0 122 | bases=7 123 | 124 | p; progress $baseprog $bases INSTCORE "Installing core packages" #1 125 | info INSTCORE "Installing core packages..." 126 | 127 | p; progress $baseprog $bases INSTCORE "Installing core packages" #2 128 | ln -sf mawk "$TARGET/usr/bin/awk" 129 | x_core_install base-passwd 130 | x_core_install base-files 131 | p; progress $baseprog $bases INSTCORE "Installing core packages" #3 132 | x_core_install dpkg 133 | 134 | if [ ! -e "$TARGET/etc/localtime" ]; then 135 | ln -sf /usr/share/zoneinfo/UTC "$TARGET/etc/localtime" 136 | fi 137 | 138 | if doing_variant fakechroot; then 139 | install_fakechroot_tools 140 | fi 141 | 142 | p; progress $baseprog $bases INSTCORE "Installing core packages" #4 143 | x_core_install $LIBC 144 | 145 | p; progress $baseprog $bases INSTCORE "Installing core packages" #5 146 | x_core_install perl-base 147 | 148 | p; progress $baseprog $bases INSTCORE "Installing core packages" #6 149 | rm "$TARGET/usr/bin/awk" 150 | x_core_install mawk 151 | 152 | p; progress $baseprog $bases INSTCORE "Installing core packages" #7 153 | if doing_variant -; then 154 | x_core_install debconf 155 | fi 156 | 157 | baseprog=0 158 | bases=$(set -- $required; echo $#) 159 | 160 | info UNPACKREQ "Unpacking required packages..." 161 | 162 | exec 7>&1 163 | 164 | smallyes '' | 165 | (repeatn 5 in_target_failmsg UNPACK_REQ_FAIL_FIVE "Failure while unpacking required packages. This will be attempted up to five times." "" \ 166 | dpkg --status-fd 8 --force-depends --unpack $(debfor $required) 8>&1 1>&7 || echo EXITCODE $?) | 167 | dpkg_progress $baseprog $bases UNPACKREQ "Unpacking required packages" UNPACKING 168 | 169 | info CONFREQ "Configuring required packages..." 170 | 171 | echo \ 172 | "#!/bin/sh 173 | exit 101" > "$TARGET/usr/sbin/policy-rc.d" 174 | chmod 755 "$TARGET/usr/sbin/policy-rc.d" 175 | 176 | mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL" 177 | echo \ 178 | "#!/bin/sh 179 | echo 180 | echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon" 181 | chmod 755 "$TARGET/sbin/start-stop-daemon" 182 | 183 | setup_dselect_method apt 184 | 185 | smallyes '' | 186 | (in_target_failmsg CONF_REQ_FAIL "Failure while configuring required packages." "" \ 187 | dpkg --status-fd 8 --configure --pending --force-configure-any --force-depends 8>&1 1>&7 || echo EXITCODE $?) | 188 | dpkg_progress $baseprog $bases CONFREQ "Configuring required packages" CONFIGURING 189 | 190 | baseprog=0 191 | bases="$(set -- $base; echo $#)" 192 | 193 | info UNPACKBASE "Unpacking the base system..." 194 | 195 | setup_available $required $base 196 | done_predeps= 197 | while predep=$(get_next_predep); do 198 | # We have to resolve dependencies of pre-dependencies manually because 199 | # dpkg --predep-package doesn't handle this. 200 | predep=$(without "$(without "$(resolve_deps $predep)" "$required")" "$done_predeps") 201 | # XXX: progress is tricky due to how dpkg_progress works 202 | # -- cjwatson 2009-07-29 203 | p; smallyes '' | 204 | in_target dpkg --force-overwrite --force-confold --skip-same-version --install $(debfor $predep) 205 | base=$(without "$base" "$predep") 206 | done_predeps="$done_predeps $predep" 207 | done 208 | 209 | smallyes '' | 210 | (repeatn 5 in_target_failmsg INST_BASE_FAIL_FIVE "Failure while installing base packages. This will be re-attempted up to five times." "" \ 211 | dpkg --status-fd 8 --force-overwrite --force-confold --skip-same-version --unpack $(debfor $base) 8>&1 1>&7 || echo EXITCODE $?) | 212 | dpkg_progress $baseprog $bases UNPACKBASE "Unpacking base system" UNPACKING 213 | 214 | info CONFBASE "Configuring the base system..." 215 | 216 | smallyes '' | 217 | (repeatn 5 in_target_failmsg CONF_BASE_FAIL_FIVE "Failure while configuring base packages. This will be re-attempted up to five times." "" \ 218 | dpkg --status-fd 8 --force-confold --skip-same-version --configure -a 8>&1 1>&7 || echo EXITCODE $?) | 219 | dpkg_progress $baseprog $bases CONFBASE "Configuring base system" CONFIGURING 220 | 221 | mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon" 222 | rm -f "$TARGET/usr/sbin/policy-rc.d" 223 | 224 | progress $bases $bases CONFBASE "Configuring base system" 225 | info BASESUCCESS "Base system installed successfully." 226 | } 227 | -------------------------------------------------------------------------------- /debian/debootstrap/jessie: -------------------------------------------------------------------------------- 1 | mirror_style release 2 | download_style apt 3 | finddebs_style from-indices 4 | variants - container fakechroot 5 | keyring /usr/share/keyrings/debian-archive-keyring.gpg 6 | 7 | if doing_variant fakechroot; then 8 | test "$FAKECHROOT" = "true" || error 1 FAKECHROOTREQ "This variant requires fakechroot environment to be started" 9 | fi 10 | 11 | case $ARCH in 12 | alpha|ia64) LIBC="libc6.1" ;; 13 | kfreebsd-*) LIBC="libc0.1" ;; 14 | hurd-*) LIBC="libc0.3" ;; 15 | *) LIBC="libc6" ;; 16 | esac 17 | 18 | work_out_debs () { 19 | # adduser in case users want to add a user to run as non-root 20 | # base-files as it has many important files 21 | # base-passwd to get user account info 22 | # bash because users will often shell in 23 | # bsdutils because it has some commands used in postinst 24 | # - particularly `logger` for `mysql-server` see 25 | # https://github.com/bitnami/minideb/issues/16 26 | # coreutils for many very common utilities 27 | # dash for a shell for scripts 28 | # debian-archive-keyring to verify apt packages 29 | # diffutils for diff as required for installing the system 30 | # (could maybe be removed after, but diffing is pretty common in debugging) 31 | # dpkg for dpkg 32 | # findutils for find as required for installing the system 33 | # grep as it is a very common debugging tool 34 | # gzip as decompressing zip is super common 35 | # hostname ? 36 | # libc-bin for ldconfig 37 | # login as su maybe used if run as non root (?) 38 | # lsb-base ? 39 | # mawk as it is used by dpkg 40 | # ncurses-base for terminfo files as docker sets TERM=xterm 41 | # see https://github.com/bitnami/minideb/issues/17 42 | # passwd for managing user accounts if run as non-root. 43 | # sed as a very commonly used tool 44 | # sysv-rc for update-rc.d, required when installing initscripts in postinsts 45 | # tar as uncompressing tarballs is super common when installing things. 46 | # tzdata for handling timezones 47 | # util-linux for getopt 48 | # mount is required for mounting /proc during debootstrap 49 | required="adduser base-files base-passwd bash bsdutils coreutils dash debian-archive-keyring diffutils dpkg findutils grep gzip hostname init-system-helpers libc-bin login lsb-base mawk ncurses-base passwd sed sysv-rc tar tzdata util-linux mount" 50 | 51 | base="apt" 52 | 53 | if doing_variant fakechroot; then 54 | # ldd.fake needs binutils 55 | required="$required binutils" 56 | fi 57 | 58 | case $MIRRORS in 59 | https://*) 60 | base="$base apt-transport-https ca-certificates" 61 | ;; 62 | esac 63 | } 64 | 65 | first_stage_install () { 66 | extract $required 67 | 68 | mkdir -p "$TARGET/var/lib/dpkg" 69 | : >"$TARGET/var/lib/dpkg/status" 70 | : >"$TARGET/var/lib/dpkg/available" 71 | 72 | setup_etc 73 | if [ ! -e "$TARGET/etc/fstab" ]; then 74 | echo '# UNCONFIGURED FSTAB FOR BASE SYSTEM' > "$TARGET/etc/fstab" 75 | chown 0:0 "$TARGET/etc/fstab"; chmod 644 "$TARGET/etc/fstab" 76 | fi 77 | 78 | setup_devices 79 | 80 | x_feign_install () { 81 | local pkg="$1" 82 | local deb="$(debfor $pkg)" 83 | local ver="$(extract_deb_field "$TARGET/$deb" Version)" 84 | 85 | mkdir -p "$TARGET/var/lib/dpkg/info" 86 | 87 | echo \ 88 | "Package: $pkg 89 | Version: $ver 90 | Maintainer: unknown 91 | Status: install ok installed" >> "$TARGET/var/lib/dpkg/status" 92 | 93 | touch "$TARGET/var/lib/dpkg/info/${pkg}.list" 94 | } 95 | 96 | x_feign_install dpkg 97 | } 98 | 99 | second_stage_install () { 100 | setup_dynamic_devices 101 | 102 | x_core_install () { 103 | smallyes '' | in_target dpkg --force-depends --install $(debfor "$@") 104 | } 105 | 106 | p () { 107 | baseprog="$(($baseprog + ${1:-1}))" 108 | } 109 | 110 | if doing_variant fakechroot; then 111 | setup_proc_fakechroot 112 | else 113 | setup_proc 114 | in_target /sbin/ldconfig 115 | fi 116 | 117 | DEBIAN_FRONTEND=noninteractive 118 | DEBCONF_NONINTERACTIVE_SEEN=true 119 | export DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN 120 | 121 | baseprog=0 122 | bases=7 123 | 124 | p; progress $baseprog $bases INSTCORE "Installing core packages" #1 125 | info INSTCORE "Installing core packages..." 126 | 127 | p; progress $baseprog $bases INSTCORE "Installing core packages" #2 128 | ln -sf mawk "$TARGET/usr/bin/awk" 129 | x_core_install base-passwd 130 | x_core_install base-files 131 | p; progress $baseprog $bases INSTCORE "Installing core packages" #3 132 | x_core_install dpkg 133 | 134 | if [ ! -e "$TARGET/etc/localtime" ]; then 135 | ln -sf /usr/share/zoneinfo/UTC "$TARGET/etc/localtime" 136 | fi 137 | 138 | if doing_variant fakechroot; then 139 | install_fakechroot_tools 140 | fi 141 | 142 | p; progress $baseprog $bases INSTCORE "Installing core packages" #4 143 | x_core_install $LIBC 144 | 145 | p; progress $baseprog $bases INSTCORE "Installing core packages" #5 146 | x_core_install perl-base 147 | 148 | p; progress $baseprog $bases INSTCORE "Installing core packages" #6 149 | rm "$TARGET/usr/bin/awk" 150 | x_core_install mawk 151 | 152 | p; progress $baseprog $bases INSTCORE "Installing core packages" #7 153 | if doing_variant -; then 154 | x_core_install debconf 155 | fi 156 | 157 | baseprog=0 158 | bases=$(set -- $required; echo $#) 159 | 160 | info UNPACKREQ "Unpacking required packages..." 161 | 162 | exec 7>&1 163 | 164 | smallyes '' | 165 | (repeatn 5 in_target_failmsg UNPACK_REQ_FAIL_FIVE "Failure while unpacking required packages. This will be attempted up to five times." "" \ 166 | dpkg --status-fd 8 --force-depends --unpack $(debfor $required) 8>&1 1>&7 || echo EXITCODE $?) | 167 | dpkg_progress $baseprog $bases UNPACKREQ "Unpacking required packages" UNPACKING 168 | 169 | info CONFREQ "Configuring required packages..." 170 | 171 | echo \ 172 | "#!/bin/sh 173 | exit 101" > "$TARGET/usr/sbin/policy-rc.d" 174 | chmod 755 "$TARGET/usr/sbin/policy-rc.d" 175 | 176 | mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL" 177 | echo \ 178 | "#!/bin/sh 179 | echo 180 | echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon" 181 | chmod 755 "$TARGET/sbin/start-stop-daemon" 182 | 183 | setup_dselect_method apt 184 | 185 | smallyes '' | 186 | (in_target_failmsg CONF_REQ_FAIL "Failure while configuring required packages." "" \ 187 | dpkg --status-fd 8 --configure --pending --force-configure-any --force-depends 8>&1 1>&7 || echo EXITCODE $?) | 188 | dpkg_progress $baseprog $bases CONFREQ "Configuring required packages" CONFIGURING 189 | 190 | baseprog=0 191 | bases="$(set -- $base; echo $#)" 192 | 193 | info UNPACKBASE "Unpacking the base system..." 194 | 195 | setup_available $required $base 196 | done_predeps= 197 | while predep=$(get_next_predep); do 198 | # We have to resolve dependencies of pre-dependencies manually because 199 | # dpkg --predep-package doesn't handle this. 200 | predep=$(without "$(without "$(resolve_deps $predep)" "$required")" "$done_predeps") 201 | # XXX: progress is tricky due to how dpkg_progress works 202 | # -- cjwatson 2009-07-29 203 | p; smallyes '' | 204 | in_target dpkg --force-overwrite --force-confold --skip-same-version --install $(debfor $predep) 205 | base=$(without "$base" "$predep") 206 | done_predeps="$done_predeps $predep" 207 | done 208 | 209 | smallyes '' | 210 | (repeatn 5 in_target_failmsg INST_BASE_FAIL_FIVE "Failure while installing base packages. This will be re-attempted up to five times." "" \ 211 | dpkg --status-fd 8 --force-overwrite --force-confold --skip-same-version --unpack $(debfor $base) 8>&1 1>&7 || echo EXITCODE $?) | 212 | dpkg_progress $baseprog $bases UNPACKBASE "Unpacking base system" UNPACKING 213 | 214 | info CONFBASE "Configuring the base system..." 215 | 216 | smallyes '' | 217 | (repeatn 5 in_target_failmsg CONF_BASE_FAIL_FIVE "Failure while configuring base packages. This will be re-attempted up to five times." "" \ 218 | dpkg --status-fd 8 --force-confold --skip-same-version --configure -a 8>&1 1>&7 || echo EXITCODE $?) | 219 | dpkg_progress $baseprog $bases CONFBASE "Configuring base system" CONFIGURING 220 | 221 | mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon" 222 | rm -f "$TARGET/usr/sbin/policy-rc.d" 223 | 224 | progress $bases $bases CONFBASE "Configuring base system" 225 | info BASESUCCESS "Base system installed successfully." 226 | } 227 | -------------------------------------------------------------------------------- /debian/debootstrap/stretch: -------------------------------------------------------------------------------- 1 | mirror_style release 2 | download_style apt 3 | finddebs_style from-indices 4 | variants - container fakechroot 5 | keyring /usr/share/keyrings/debian-archive-keyring.gpg 6 | 7 | if doing_variant fakechroot; then 8 | test "$FAKECHROOT" = "true" || error 1 FAKECHROOTREQ "This variant requires fakechroot environment to be started" 9 | fi 10 | 11 | case $ARCH in 12 | alpha|ia64) LIBC="libc6.1" ;; 13 | kfreebsd-*) LIBC="libc0.1" ;; 14 | hurd-*) LIBC="libc0.3" ;; 15 | *) LIBC="libc6" ;; 16 | esac 17 | 18 | work_out_debs () { 19 | # adduser in case users want to add a user to run as non-root 20 | # base-files as it has many important files 21 | # base-passwd to get user account info 22 | # bash because users will often shell in 23 | # bsdutils because it has some commands used in postinst 24 | # - particularly `logger` for `mysql-server` see 25 | # https://github.com/bitnami/minideb/issues/16 26 | # coreutils for many very common utilities 27 | # dash for a shell for scripts 28 | # debian-archive-keyring to verify apt packages 29 | # diffutils for diff as required for installing the system 30 | # (could maybe be removed after, but diffing is pretty common in debugging) 31 | # dpkg for dpkg 32 | # findutils for find as required for installing the system 33 | # grep as it is a very common debugging tool 34 | # gzip as decompressing zip is super common 35 | # hostname ? 36 | # libc-bin for ldconfig 37 | # login as su maybe used if run as non root (?) 38 | # lsb-base ? 39 | # mawk as it is used by dpkg 40 | # ncurses-base for terminfo files as docker sets TERM=xterm 41 | # see https://github.com/bitnami/minideb/issues/17 42 | # passwd for managing user accounts if run as non-root. 43 | # sed as a very commonly used tool 44 | # sysv-rc for update-rc.d, required when installing initscripts in postinsts 45 | # tar as uncompressing tarballs is super common when installing things. 46 | # tzdata for handling timezones 47 | # util-linux for getopt 48 | # mount is required for mounting /proc during debootstrap 49 | required="adduser base-files base-passwd bash bsdutils coreutils dash debian-archive-keyring diffutils dpkg findutils grep gzip hostname init-system-helpers libc-bin login lsb-base mawk ncurses-base passwd sed sysv-rc tar tzdata util-linux mount" 50 | 51 | base="apt" 52 | 53 | if doing_variant fakechroot; then 54 | # ldd.fake needs binutils 55 | required="$required binutils" 56 | fi 57 | 58 | case $MIRRORS in 59 | https://*) 60 | base="$base apt-transport-https ca-certificates" 61 | ;; 62 | esac 63 | } 64 | 65 | first_stage_install () { 66 | extract $required 67 | 68 | mkdir -p "$TARGET/var/lib/dpkg" 69 | : >"$TARGET/var/lib/dpkg/status" 70 | : >"$TARGET/var/lib/dpkg/available" 71 | 72 | setup_etc 73 | if [ ! -e "$TARGET/etc/fstab" ]; then 74 | echo '# UNCONFIGURED FSTAB FOR BASE SYSTEM' > "$TARGET/etc/fstab" 75 | chown 0:0 "$TARGET/etc/fstab"; chmod 644 "$TARGET/etc/fstab" 76 | fi 77 | 78 | setup_devices 79 | 80 | x_feign_install () { 81 | local pkg="$1" 82 | local deb="$(debfor $pkg)" 83 | local ver="$(extract_deb_field "$TARGET/$deb" Version)" 84 | 85 | mkdir -p "$TARGET/var/lib/dpkg/info" 86 | 87 | echo \ 88 | "Package: $pkg 89 | Version: $ver 90 | Maintainer: unknown 91 | Status: install ok installed" >> "$TARGET/var/lib/dpkg/status" 92 | 93 | touch "$TARGET/var/lib/dpkg/info/${pkg}.list" 94 | } 95 | 96 | x_feign_install dpkg 97 | } 98 | 99 | second_stage_install () { 100 | setup_dynamic_devices 101 | 102 | x_core_install () { 103 | smallyes '' | in_target dpkg --force-depends --install $(debfor "$@") 104 | } 105 | 106 | p () { 107 | baseprog="$(($baseprog + ${1:-1}))" 108 | } 109 | 110 | if doing_variant fakechroot; then 111 | setup_proc_fakechroot 112 | else 113 | setup_proc 114 | in_target /sbin/ldconfig 115 | fi 116 | 117 | DEBIAN_FRONTEND=noninteractive 118 | DEBCONF_NONINTERACTIVE_SEEN=true 119 | export DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN 120 | 121 | baseprog=0 122 | bases=7 123 | 124 | p; progress $baseprog $bases INSTCORE "Installing core packages" #1 125 | info INSTCORE "Installing core packages..." 126 | 127 | p; progress $baseprog $bases INSTCORE "Installing core packages" #2 128 | ln -sf mawk "$TARGET/usr/bin/awk" 129 | x_core_install base-passwd 130 | x_core_install base-files 131 | p; progress $baseprog $bases INSTCORE "Installing core packages" #3 132 | x_core_install dpkg 133 | 134 | if [ ! -e "$TARGET/etc/localtime" ]; then 135 | ln -sf /usr/share/zoneinfo/UTC "$TARGET/etc/localtime" 136 | fi 137 | 138 | if doing_variant fakechroot; then 139 | install_fakechroot_tools 140 | fi 141 | 142 | p; progress $baseprog $bases INSTCORE "Installing core packages" #4 143 | x_core_install $LIBC 144 | 145 | p; progress $baseprog $bases INSTCORE "Installing core packages" #5 146 | x_core_install perl-base 147 | 148 | p; progress $baseprog $bases INSTCORE "Installing core packages" #6 149 | rm "$TARGET/usr/bin/awk" 150 | x_core_install mawk 151 | 152 | p; progress $baseprog $bases INSTCORE "Installing core packages" #7 153 | if doing_variant -; then 154 | x_core_install debconf 155 | fi 156 | 157 | baseprog=0 158 | bases=$(set -- $required; echo $#) 159 | 160 | info UNPACKREQ "Unpacking required packages..." 161 | 162 | exec 7>&1 163 | 164 | smallyes '' | 165 | (repeatn 5 in_target_failmsg UNPACK_REQ_FAIL_FIVE "Failure while unpacking required packages. This will be attempted up to five times." "" \ 166 | dpkg --status-fd 8 --force-depends --unpack $(debfor $required) 8>&1 1>&7 || echo EXITCODE $?) | 167 | dpkg_progress $baseprog $bases UNPACKREQ "Unpacking required packages" UNPACKING 168 | 169 | info CONFREQ "Configuring required packages..." 170 | 171 | echo \ 172 | "#!/bin/sh 173 | exit 101" > "$TARGET/usr/sbin/policy-rc.d" 174 | chmod 755 "$TARGET/usr/sbin/policy-rc.d" 175 | 176 | mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL" 177 | echo \ 178 | "#!/bin/sh 179 | echo 180 | echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon" 181 | chmod 755 "$TARGET/sbin/start-stop-daemon" 182 | 183 | setup_dselect_method apt 184 | 185 | smallyes '' | 186 | (in_target_failmsg CONF_REQ_FAIL "Failure while configuring required packages." "" \ 187 | dpkg --status-fd 8 --configure --pending --force-configure-any --force-depends 8>&1 1>&7 || echo EXITCODE $?) | 188 | dpkg_progress $baseprog $bases CONFREQ "Configuring required packages" CONFIGURING 189 | 190 | baseprog=0 191 | bases="$(set -- $base; echo $#)" 192 | 193 | info UNPACKBASE "Unpacking the base system..." 194 | 195 | setup_available $required $base 196 | done_predeps= 197 | while predep=$(get_next_predep); do 198 | # We have to resolve dependencies of pre-dependencies manually because 199 | # dpkg --predep-package doesn't handle this. 200 | predep=$(without "$(without "$(resolve_deps $predep)" "$required")" "$done_predeps") 201 | # XXX: progress is tricky due to how dpkg_progress works 202 | # -- cjwatson 2009-07-29 203 | p; smallyes '' | 204 | in_target dpkg --force-overwrite --force-confold --skip-same-version --install $(debfor $predep) 205 | base=$(without "$base" "$predep") 206 | done_predeps="$done_predeps $predep" 207 | done 208 | 209 | smallyes '' | 210 | (repeatn 5 in_target_failmsg INST_BASE_FAIL_FIVE "Failure while installing base packages. This will be re-attempted up to five times." "" \ 211 | dpkg --status-fd 8 --force-overwrite --force-confold --skip-same-version --unpack $(debfor $base) 8>&1 1>&7 || echo EXITCODE $?) | 212 | dpkg_progress $baseprog $bases UNPACKBASE "Unpacking base system" UNPACKING 213 | 214 | info CONFBASE "Configuring the base system..." 215 | 216 | smallyes '' | 217 | (repeatn 5 in_target_failmsg CONF_BASE_FAIL_FIVE "Failure while configuring base packages. This will be re-attempted up to five times." "" \ 218 | dpkg --status-fd 8 --force-confold --skip-same-version --configure -a 8>&1 1>&7 || echo EXITCODE $?) | 219 | dpkg_progress $baseprog $bases CONFBASE "Configuring base system" CONFIGURING 220 | 221 | mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon" 222 | rm -f "$TARGET/usr/sbin/policy-rc.d" 223 | 224 | progress $bases $bases CONFBASE "Configuring base system" 225 | info BASESUCCESS "Base system installed successfully." 226 | } 227 | -------------------------------------------------------------------------------- /debian/debootstrap/unstable: -------------------------------------------------------------------------------- 1 | mirror_style release 2 | download_style apt 3 | finddebs_style from-indices 4 | variants - container fakechroot 5 | keyring /usr/share/keyrings/debian-archive-keyring.gpg 6 | 7 | if doing_variant fakechroot; then 8 | test "$FAKECHROOT" = "true" || error 1 FAKECHROOTREQ "This variant requires fakechroot environment to be started" 9 | fi 10 | 11 | case $ARCH in 12 | alpha|ia64) LIBC="libc6.1" ;; 13 | kfreebsd-*) LIBC="libc0.1" ;; 14 | hurd-*) LIBC="libc0.3" ;; 15 | *) LIBC="libc6" ;; 16 | esac 17 | 18 | work_out_debs () { 19 | # adduser in case users want to add a user to run as non-root 20 | # base-files as it has many important files 21 | # base-passwd to get user account info 22 | # bash because users will often shell in 23 | # bsdutils because it has some commands used in postinst 24 | # - particularly `logger` for `mysql-server` see 25 | # https://github.com/bitnami/minideb/issues/16 26 | # coreutils for many very common utilities 27 | # dash for a shell for scripts 28 | # debian-archive-keyring to verify apt packages 29 | # diffutils for diff as required for installing the system 30 | # (could maybe be removed after, but diffing is pretty common in debugging) 31 | # dpkg for dpkg 32 | # findutils for find as required for installing the system 33 | # grep as it is a very common debugging tool 34 | # gzip as decompressing zip is super common 35 | # hostname ? 36 | # libc-bin for ldconfig 37 | # login as su maybe used if run as non root (?) 38 | # lsb-base ? 39 | # mawk as it is used by dpkg 40 | # ncurses-base for terminfo files as docker sets TERM=xterm 41 | # see https://github.com/bitnami/minideb/issues/17 42 | # passwd for managing user accounts if run as non-root. 43 | # sed as a very commonly used tool 44 | # sysv-rc for update-rc.d, required when installing initscripts in postinsts 45 | # tar as uncompressing tarballs is super common when installing things. 46 | # tzdata for handling timezones 47 | # util-linux for getopt 48 | # mount is required for mounting /proc during debootstrap 49 | required="adduser base-files base-passwd bash bsdutils coreutils dash debian-archive-keyring diffutils dpkg findutils grep gzip hostname init-system-helpers libc-bin login lsb-base mawk ncurses-base passwd sed sysv-rc tar tzdata util-linux mount" 50 | 51 | base="apt" 52 | 53 | if doing_variant fakechroot; then 54 | # ldd.fake needs binutils 55 | required="$required binutils" 56 | fi 57 | 58 | case $MIRRORS in 59 | https://*) 60 | base="$base apt-transport-https ca-certificates" 61 | ;; 62 | esac 63 | } 64 | 65 | first_stage_install () { 66 | extract $required 67 | 68 | mkdir -p "$TARGET/var/lib/dpkg" 69 | : >"$TARGET/var/lib/dpkg/status" 70 | : >"$TARGET/var/lib/dpkg/available" 71 | 72 | setup_etc 73 | if [ ! -e "$TARGET/etc/fstab" ]; then 74 | echo '# UNCONFIGURED FSTAB FOR BASE SYSTEM' > "$TARGET/etc/fstab" 75 | chown 0:0 "$TARGET/etc/fstab"; chmod 644 "$TARGET/etc/fstab" 76 | fi 77 | 78 | setup_devices 79 | 80 | x_feign_install () { 81 | local pkg="$1" 82 | local deb="$(debfor $pkg)" 83 | local ver="$(extract_deb_field "$TARGET/$deb" Version)" 84 | 85 | mkdir -p "$TARGET/var/lib/dpkg/info" 86 | 87 | echo \ 88 | "Package: $pkg 89 | Version: $ver 90 | Maintainer: unknown 91 | Status: install ok installed" >> "$TARGET/var/lib/dpkg/status" 92 | 93 | touch "$TARGET/var/lib/dpkg/info/${pkg}.list" 94 | } 95 | 96 | x_feign_install dpkg 97 | } 98 | 99 | second_stage_install () { 100 | setup_dynamic_devices 101 | 102 | x_core_install () { 103 | smallyes '' | in_target dpkg --force-depends --install $(debfor "$@") 104 | } 105 | 106 | p () { 107 | baseprog="$(($baseprog + ${1:-1}))" 108 | } 109 | 110 | if doing_variant fakechroot; then 111 | setup_proc_fakechroot 112 | else 113 | setup_proc 114 | in_target /sbin/ldconfig 115 | fi 116 | 117 | DEBIAN_FRONTEND=noninteractive 118 | DEBCONF_NONINTERACTIVE_SEEN=true 119 | export DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN 120 | 121 | baseprog=0 122 | bases=7 123 | 124 | p; progress $baseprog $bases INSTCORE "Installing core packages" #1 125 | info INSTCORE "Installing core packages..." 126 | 127 | p; progress $baseprog $bases INSTCORE "Installing core packages" #2 128 | ln -sf mawk "$TARGET/usr/bin/awk" 129 | x_core_install base-passwd 130 | x_core_install base-files 131 | p; progress $baseprog $bases INSTCORE "Installing core packages" #3 132 | x_core_install dpkg 133 | 134 | if [ ! -e "$TARGET/etc/localtime" ]; then 135 | ln -sf /usr/share/zoneinfo/UTC "$TARGET/etc/localtime" 136 | fi 137 | 138 | if doing_variant fakechroot; then 139 | install_fakechroot_tools 140 | fi 141 | 142 | p; progress $baseprog $bases INSTCORE "Installing core packages" #4 143 | x_core_install $LIBC 144 | 145 | p; progress $baseprog $bases INSTCORE "Installing core packages" #5 146 | x_core_install perl-base 147 | 148 | p; progress $baseprog $bases INSTCORE "Installing core packages" #6 149 | rm "$TARGET/usr/bin/awk" 150 | x_core_install mawk 151 | 152 | p; progress $baseprog $bases INSTCORE "Installing core packages" #7 153 | if doing_variant -; then 154 | x_core_install debconf 155 | fi 156 | 157 | baseprog=0 158 | bases=$(set -- $required; echo $#) 159 | 160 | info UNPACKREQ "Unpacking required packages..." 161 | 162 | exec 7>&1 163 | 164 | smallyes '' | 165 | (repeatn 5 in_target_failmsg UNPACK_REQ_FAIL_FIVE "Failure while unpacking required packages. This will be attempted up to five times." "" \ 166 | dpkg --status-fd 8 --force-depends --unpack $(debfor $required) 8>&1 1>&7 || echo EXITCODE $?) | 167 | dpkg_progress $baseprog $bases UNPACKREQ "Unpacking required packages" UNPACKING 168 | 169 | info CONFREQ "Configuring required packages..." 170 | 171 | echo \ 172 | "#!/bin/sh 173 | exit 101" > "$TARGET/usr/sbin/policy-rc.d" 174 | chmod 755 "$TARGET/usr/sbin/policy-rc.d" 175 | 176 | mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL" 177 | echo \ 178 | "#!/bin/sh 179 | echo 180 | echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon" 181 | chmod 755 "$TARGET/sbin/start-stop-daemon" 182 | 183 | setup_dselect_method apt 184 | 185 | smallyes '' | 186 | (in_target_failmsg CONF_REQ_FAIL "Failure while configuring required packages." "" \ 187 | dpkg --status-fd 8 --configure --pending --force-configure-any --force-depends 8>&1 1>&7 || echo EXITCODE $?) | 188 | dpkg_progress $baseprog $bases CONFREQ "Configuring required packages" CONFIGURING 189 | 190 | baseprog=0 191 | bases="$(set -- $base; echo $#)" 192 | 193 | info UNPACKBASE "Unpacking the base system..." 194 | 195 | setup_available $required $base 196 | done_predeps= 197 | while predep=$(get_next_predep); do 198 | # We have to resolve dependencies of pre-dependencies manually because 199 | # dpkg --predep-package doesn't handle this. 200 | predep=$(without "$(without "$(resolve_deps $predep)" "$required")" "$done_predeps") 201 | # XXX: progress is tricky due to how dpkg_progress works 202 | # -- cjwatson 2009-07-29 203 | p; smallyes '' | 204 | in_target dpkg --force-overwrite --force-confold --skip-same-version --install $(debfor $predep) 205 | base=$(without "$base" "$predep") 206 | done_predeps="$done_predeps $predep" 207 | done 208 | 209 | smallyes '' | 210 | (repeatn 5 in_target_failmsg INST_BASE_FAIL_FIVE "Failure while installing base packages. This will be re-attempted up to five times." "" \ 211 | dpkg --status-fd 8 --force-overwrite --force-confold --skip-same-version --unpack $(debfor $base) 8>&1 1>&7 || echo EXITCODE $?) | 212 | dpkg_progress $baseprog $bases UNPACKBASE "Unpacking base system" UNPACKING 213 | 214 | info CONFBASE "Configuring the base system..." 215 | 216 | smallyes '' | 217 | (repeatn 5 in_target_failmsg CONF_BASE_FAIL_FIVE "Failure while configuring base packages. This will be re-attempted up to five times." "" \ 218 | dpkg --status-fd 8 --force-confold --skip-same-version --configure -a 8>&1 1>&7 || echo EXITCODE $?) | 219 | dpkg_progress $baseprog $bases CONFBASE "Configuring base system" CONFIGURING 220 | 221 | mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon" 222 | rm -f "$TARGET/usr/sbin/policy-rc.d" 223 | 224 | progress $bases $bases CONFBASE "Configuring base system" 225 | info BASESUCCESS "Base system installed successfully." 226 | } 227 | -------------------------------------------------------------------------------- /debian/debootstrap/wheezy: -------------------------------------------------------------------------------- 1 | mirror_style release 2 | download_style apt 3 | finddebs_style from-indices 4 | variants - container fakechroot 5 | keyring /usr/share/keyrings/debian-archive-keyring.gpg 6 | 7 | if doing_variant fakechroot; then 8 | test "$FAKECHROOT" = "true" || error 1 FAKECHROOTREQ "This variant requires fakechroot environment to be started" 9 | fi 10 | 11 | case $ARCH in 12 | alpha|ia64) LIBC="libc6.1" ;; 13 | kfreebsd-*) LIBC="libc0.1" ;; 14 | hurd-*) LIBC="libc0.3" ;; 15 | *) LIBC="libc6" ;; 16 | esac 17 | 18 | work_out_debs () { 19 | # adduser in case users want to add a user to run as non-root 20 | # base-files as it has many important files 21 | # base-passwd to get user account info 22 | # bash because users will often shell in 23 | # bsdutils because it has some commands used in postinst 24 | # - particularly `logger` for `mysql-server` see 25 | # https://github.com/bitnami/minideb/issues/16 26 | # coreutils for many very common utilities 27 | # dash for a shell for scripts 28 | # debian-archive-keyring to verify apt packages 29 | # diffutils for diff as required for installing the system 30 | # (could maybe be removed after, but diffing is pretty common in debugging) 31 | # dpkg for dpkg 32 | # findutils for find as required for installing the system 33 | # grep as it is a very common debugging tool 34 | # gzip as decompressing zip is super common 35 | # hostname ? 36 | # libc-bin for ldconfig 37 | # login as su maybe used if run as non root (?) 38 | # lsb-base ? 39 | # mawk as it is used by dpkg 40 | # ncurses-base for terminfo files as docker sets TERM=xterm 41 | # see https://github.com/bitnami/minideb/issues/17 42 | # passwd for managing user accounts if run as non-root. 43 | # sed as a very commonly used tool 44 | # sysv-rc for update-rc.d, required when installing initscripts in postinsts 45 | # tar as uncompressing tarballs is super common when installing things. 46 | # tzdata for handling timezones 47 | # util-linux for getopt 48 | # mount is required for mounting /proc during debootstrap 49 | required="adduser base-files base-passwd bash bsdutils coreutils dash debian-archive-keyring diffutils dpkg findutils grep gzip hostname init-system-helpers libc-bin login lsb-base mawk ncurses-base passwd sed sysv-rc tar tzdata util-linux mount" 50 | 51 | base="apt" 52 | 53 | if doing_variant fakechroot; then 54 | # ldd.fake needs binutils 55 | required="$required binutils" 56 | fi 57 | 58 | case $MIRRORS in 59 | https://*) 60 | base="$base apt-transport-https ca-certificates" 61 | ;; 62 | esac 63 | } 64 | 65 | first_stage_install () { 66 | extract $required 67 | 68 | mkdir -p "$TARGET/var/lib/dpkg" 69 | : >"$TARGET/var/lib/dpkg/status" 70 | : >"$TARGET/var/lib/dpkg/available" 71 | 72 | setup_etc 73 | if [ ! -e "$TARGET/etc/fstab" ]; then 74 | echo '# UNCONFIGURED FSTAB FOR BASE SYSTEM' > "$TARGET/etc/fstab" 75 | chown 0:0 "$TARGET/etc/fstab"; chmod 644 "$TARGET/etc/fstab" 76 | fi 77 | 78 | setup_devices 79 | 80 | x_feign_install () { 81 | local pkg="$1" 82 | local deb="$(debfor $pkg)" 83 | local ver="$(extract_deb_field "$TARGET/$deb" Version)" 84 | 85 | mkdir -p "$TARGET/var/lib/dpkg/info" 86 | 87 | echo \ 88 | "Package: $pkg 89 | Version: $ver 90 | Maintainer: unknown 91 | Status: install ok installed" >> "$TARGET/var/lib/dpkg/status" 92 | 93 | touch "$TARGET/var/lib/dpkg/info/${pkg}.list" 94 | } 95 | 96 | x_feign_install dpkg 97 | } 98 | 99 | second_stage_install () { 100 | setup_dynamic_devices 101 | 102 | x_core_install () { 103 | smallyes '' | in_target dpkg --force-depends --install $(debfor "$@") 104 | } 105 | 106 | p () { 107 | baseprog="$(($baseprog + ${1:-1}))" 108 | } 109 | 110 | if doing_variant fakechroot; then 111 | setup_proc_fakechroot 112 | else 113 | setup_proc 114 | in_target /sbin/ldconfig 115 | fi 116 | 117 | DEBIAN_FRONTEND=noninteractive 118 | DEBCONF_NONINTERACTIVE_SEEN=true 119 | export DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN 120 | 121 | baseprog=0 122 | bases=7 123 | 124 | p; progress $baseprog $bases INSTCORE "Installing core packages" #1 125 | info INSTCORE "Installing core packages..." 126 | 127 | p; progress $baseprog $bases INSTCORE "Installing core packages" #2 128 | ln -sf mawk "$TARGET/usr/bin/awk" 129 | x_core_install base-passwd 130 | x_core_install base-files 131 | p; progress $baseprog $bases INSTCORE "Installing core packages" #3 132 | x_core_install dpkg 133 | 134 | if [ ! -e "$TARGET/etc/localtime" ]; then 135 | ln -sf /usr/share/zoneinfo/UTC "$TARGET/etc/localtime" 136 | fi 137 | 138 | if doing_variant fakechroot; then 139 | install_fakechroot_tools 140 | fi 141 | 142 | p; progress $baseprog $bases INSTCORE "Installing core packages" #4 143 | x_core_install $LIBC 144 | 145 | p; progress $baseprog $bases INSTCORE "Installing core packages" #5 146 | x_core_install perl-base 147 | 148 | p; progress $baseprog $bases INSTCORE "Installing core packages" #6 149 | rm "$TARGET/usr/bin/awk" 150 | x_core_install mawk 151 | 152 | p; progress $baseprog $bases INSTCORE "Installing core packages" #7 153 | if doing_variant -; then 154 | x_core_install debconf 155 | fi 156 | 157 | baseprog=0 158 | bases=$(set -- $required; echo $#) 159 | 160 | info UNPACKREQ "Unpacking required packages..." 161 | 162 | exec 7>&1 163 | 164 | smallyes '' | 165 | (repeatn 5 in_target_failmsg UNPACK_REQ_FAIL_FIVE "Failure while unpacking required packages. This will be attempted up to five times." "" \ 166 | dpkg --status-fd 8 --force-depends --unpack $(debfor $required) 8>&1 1>&7 || echo EXITCODE $?) | 167 | dpkg_progress $baseprog $bases UNPACKREQ "Unpacking required packages" UNPACKING 168 | 169 | info CONFREQ "Configuring required packages..." 170 | 171 | echo \ 172 | "#!/bin/sh 173 | exit 101" > "$TARGET/usr/sbin/policy-rc.d" 174 | chmod 755 "$TARGET/usr/sbin/policy-rc.d" 175 | 176 | mv "$TARGET/sbin/start-stop-daemon" "$TARGET/sbin/start-stop-daemon.REAL" 177 | echo \ 178 | "#!/bin/sh 179 | echo 180 | echo \"Warning: Fake start-stop-daemon called, doing nothing\"" > "$TARGET/sbin/start-stop-daemon" 181 | chmod 755 "$TARGET/sbin/start-stop-daemon" 182 | 183 | setup_dselect_method apt 184 | 185 | smallyes '' | 186 | (in_target_failmsg CONF_REQ_FAIL "Failure while configuring required packages." "" \ 187 | dpkg --status-fd 8 --configure --pending --force-configure-any --force-depends 8>&1 1>&7 || echo EXITCODE $?) | 188 | dpkg_progress $baseprog $bases CONFREQ "Configuring required packages" CONFIGURING 189 | 190 | baseprog=0 191 | bases="$(set -- $base; echo $#)" 192 | 193 | info UNPACKBASE "Unpacking the base system..." 194 | 195 | setup_available $required $base 196 | done_predeps= 197 | while predep=$(get_next_predep); do 198 | # We have to resolve dependencies of pre-dependencies manually because 199 | # dpkg --predep-package doesn't handle this. 200 | predep=$(without "$(without "$(resolve_deps $predep)" "$required")" "$done_predeps") 201 | # XXX: progress is tricky due to how dpkg_progress works 202 | # -- cjwatson 2009-07-29 203 | p; smallyes '' | 204 | in_target dpkg --force-overwrite --force-confold --skip-same-version --install $(debfor $predep) 205 | base=$(without "$base" "$predep") 206 | done_predeps="$done_predeps $predep" 207 | done 208 | 209 | smallyes '' | 210 | (repeatn 5 in_target_failmsg INST_BASE_FAIL_FIVE "Failure while installing base packages. This will be re-attempted up to five times." "" \ 211 | dpkg --status-fd 8 --force-overwrite --force-confold --skip-same-version --unpack $(debfor $base) 8>&1 1>&7 || echo EXITCODE $?) | 212 | dpkg_progress $baseprog $bases UNPACKBASE "Unpacking base system" UNPACKING 213 | 214 | info CONFBASE "Configuring the base system..." 215 | 216 | smallyes '' | 217 | (repeatn 5 in_target_failmsg CONF_BASE_FAIL_FIVE "Failure while configuring base packages. This will be re-attempted up to five times." "" \ 218 | dpkg --status-fd 8 --force-confold --skip-same-version --configure -a 8>&1 1>&7 || echo EXITCODE $?) | 219 | dpkg_progress $baseprog $bases CONFBASE "Configuring base system" CONFIGURING 220 | 221 | mv "$TARGET/sbin/start-stop-daemon.REAL" "$TARGET/sbin/start-stop-daemon" 222 | rm -f "$TARGET/usr/sbin/policy-rc.d" 223 | 224 | progress $bases $bases CONFBASE "Configuring base system" 225 | info BASESUCCESS "Base system installed successfully." 226 | } 227 | -------------------------------------------------------------------------------- /debian/keys/buster.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | 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 51 | idjr8CGNeffTyeMJbSniGisGOkhiX9TLbz8ufw== 52 | =a2kx 53 | -----END PGP PUBLIC KEY BLOCK----- 54 | -------------------------------------------------------------------------------- /debian/keys/debian-archive-keyring.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avkcode/container-tools/0f10c880a58c04ddc991b0a0d77b66670bf0a63d/debian/keys/debian-archive-keyring.gpg -------------------------------------------------------------------------------- /debian/keys/unstable.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQINBFyy5ecBEACxXGKUyi5dFjPhEFoz3IwKlVfDxySVg+hlhcUEO657UHf/7Ba5 4 | wr9eHxjlbpxetAymSNnptgh8oaJWcokr9UjeaTbKrYGpRra7Wd1W+f++9tF7BVvV 5 | +AWBaltD5NDuq+eQ7kj72oeMa7KAr4702ZokLgiTsS9dPeDAodx3/jMuV9VxlJ7q 6 | w07bAoUdzhlPBcII3MOCMfQmtwIg27/qqekeOnrGtNwscugwVqcBATxRZ1wNAebJ 7 | 60FH9FQOtPZJnuv/q3KXqoneuSMKiBKferQhLXDG/1fUyojNF9Dcae+HmHAZmVsV 8 | K8cHQwgSICWOgWOKVHUH0YHYvElhNIWayaw1EswEW3WMa0F4tY+EDNHEII1TGOxc 9 | X9VzbGT998Hiuf9iJuWuCgYZ75XGA/tUooOwLE77lxPGpTtLL0tr/lTJOkfwxVeY 10 | ERH1LranSQhZAXDHozKPylGo2vLxfA4WNKfaC7Mgq2WKpDWjYtF4kO6/Eiyoiq8L 11 | DqOkCtvt84PFoXEGMk3I1yd7d3bhIUwsgt6nkvn54xebJwVe5aK4MM7qCNZAm+7i 12 | 94iZjXTH9wUWX27n9UESqYeHjer1L0m/yL8sn4ceCMzpri2HsI71URwJp47GJTSV 13 | 6oAm7NJkiT5Oihcex/tvObZZXZZNqtwROBCkBcdb4Ii3upIfx8uQ3WBkSQARAQAB 14 | iQJOBB8BCgA4FiEEgNFYI7f9FWH597zd3DDXwjy7q+4FAlyy5mwXDIABgOl28UpQ 15 | ikjpyj/pvDciUsoc+WQCBwAACgkQ3DDXwjy7q+7u/g//Wzz20dlQymfkrtvgWAXN 16 | 8qw6ifkQtd/kNu61A5u5MGg/EViFnmvZdtYRentf3qnsDl3ZgjYhHMJ5hLVG16Gb 17 | 2nrkpQQe6rBX26PMkg/wP5uebUnPQscEO0KpVlJBppO4/rmJNKsphsRYCkgbZORM 18 | LyTRijrN+NJw3Lirk59ykkWyu0PQN0by+aDMOjg4Qt8vfpNxeeEBtCg7wk5XuArZ 19 | mDwcjqazkXn04l74LRzXynK2HFakROCWZQQxl87gpFXAzcdualbenazYI3nWcpPM 20 | taLvOoWpse4jM2c4UC9fX+PLOCOh01POMu/7+omeKfuSLJ77ngS7jkCdbn8y469e 21 | EBFh5tGD2piNg3IgSFjGFOIKt8eOOYQJ5dYLCYpDQ12qO3B/TnRiIwWGDPWg3wxZ 22 | UEkVS+ZkqZcBe3qIqEQ4r/ZgG2vByWdiKDEYGIk6vITOP9SBzWE29M883oAvifcG 23 | 3cTwyODl06RMe/DJkZwMxbti0qn2Fpw6T4kozVVI3wbmuLm7kShcTxeE4volP44c 24 | 3mOcqIyXIoOQeCLHy34SmYkzmSJ7iE32u6V4hzvPOtfxFbR6VUKOGvFCGUTLfvZr 25 | AqF2PiUWw9B/bXkD6j7js7eclYz9ClgDnW8p5HzA4xVoVAvZISNbwxtiwflplbYT 26 | 6t1Mv1sU2iyjjrncY2AYV1mJAk4EHwEKADgWIQSA0Vgjt/0VYfn3vN3cMNfCPLur 27 | 7gUCXLLmbBcMgAH7+r21QbXclVvZum7bFs9bsSUlxAIHAAAKCRDcMNfCPLur7ihB 28 | D/4iace5p4gK5MTRNTibKNktYfpOr47BccPGdfeEx+PrVXPHAvFVoo6cwTBa0VeS 29 | n8jXkosgwlXREUTsXFTWq0XFOKBg1OLzofKQyxfyYZLM4ge2VAGuI20HuwnAVHUU 30 | /+8BIzH31CJmvsehWIhALaCxA7RbI01aREpiDJoiBNppHCqwXBRxzk3y7Shmo4pt 31 | J+joRw4x9OZXjBC1y4q70bafOufglKGU11qMDqTan9LpbVT8eN/7xLuGQsUC+Nt5 32 | ZB/UZkN7shfHiI8bEOTfR9hawf83i/ErAv3PhFmcI9D9SAe11PYGTYwZtGs6Osnv 33 | SXyJNyxvanaFbNfowEUou4NGGdRMXff6W3qe7SQG976SHmJtHB5V5QlO9gVxU5TC 34 | TQc1IL7+JJRhJN83Yo/CnOo6xeY0/jlhZDvVFylGuHDe2L87Q4GqU4ztwrq6KYPA 35 | OuPCGrDTo6Dzc0+WAiZfnrtx11qSawa6hlP0pJdjw09fhBaugrdPyIr23b0iMwp+ 36 | Q8mMaqU8ud4Sfae8KuMvcaNF5dCNe4qJ3xVfeQCkZIsFVSWdq8LHxmQoVZYH+ZsQ 37 | 7QzjKZT5s6sb5We7scGYm6O0+1SzT0j4IoiXM39kovzmq40eEZktOm0l7qmDO5vW 38 | 2DcMSdFrf9bY4yP0/XiCgKIntl6xKC8FP6lBYl+fd4Jq1IkCTgQfAQoAOBYhBIDR 39 | WCO3/RVh+fe83dww18I8u6vuBQJcsuZsFwyAAYyCPe0QqoBBY54SEFrOjW4MFKRw 40 | AgcAAAoJENww18I8u6vu6IIP/RwycYXi/0bHlthWvS5dAfWlpkQBuG5ZZmxCgw0O 41 | meTFPrIAMk2TZ7mgeiPGetwmvze+5QeRmy4zdSZfyaQWxcWoIE+oUaWEARLlSGIT 42 | nDVn6fiAgjcqauT3Sw3EWp2UAVIvJOoz59aZI+msdglI82eSO+v/XoZ/Bk3KrwrA 43 | ClCqsPfInXdodLeBbDxQ+CJGGjq87sjS6DM8LZFR6Y3rcJf9QbGSU1ZG+bjNb4nq 44 | de29eIqhrJPcfh4p12ADNLUf0MFWh8KDkVOy9cqJH/GeYX3kPxl8cDD6s5PwEsrc 45 | TIa1Iaw7cYSxRRZQJYeCf9//2kn4xQOzFwSoVDHLjg4tTgctLzcmiebqZAtoZGLA 46 | QGDq2SrnPc9vK3z8VMgzrJM1pNkLrhAvTZtyyw85bq/SXUfymPnWDhk5071v6yfn 47 | IMLtvzgA+FcybD6mRLC1tUFhfeqqVi5zbw1haunGnwodSTw/z2BcgR9fdCGA8ebv 48 | Iwh8txQsDHNG10E8dWwF8pe/e8uSdagmITTE9QYN04rV/RRMY6WJ8+2pz12XQZmA 49 | 18BPljP2VIHZcBg5Cm2sSgjNA/rpwlGtAxA+ztimwnV39p90BAEVUco8AXXM9cBa 50 | ya2pxNf5U0hj6xMG27FqIcdmmyKlys2m6kPLDuxrF0hPBIa3WM5jEKXercrsMGC+ 51 | x9VoiQJOBB8BCgA4FiEEgNFYI7f9FWH597zd3DDXwjy7q+4FAlyy5mwXDIABMJkR 52 | vqlm0GEwUwRXEbTl/xWw/YICBwAACgkQ3DDXwjy7q+6H7w/+OLbg5w8pGGnm1t0I 53 | 2QoLVKz3bNYLf0aJ5SwODYjXnQbLgcEjct/4gexTy3ahPR6zsX2cq0BGXH80A2nT 54 | g9MP20BUOjtQnGjRozn9FotTOi5HsxoyIBcP5pfk2zcfcskpTJchqVhB5QXmw+vl 55 | CIOtjSgLjrSPmRnhHqKR8bjMzvwo+jjCwTlWVBtjU9UnA1jRhvHzTp8SLC4HHY31 56 | yAiU6FbAlthC0UvCcw/c0FxEacZiy4tDYJUehV1e2tdwHf82yRamZq/wnU6iEM9I 57 | KUNcxHKgpUxwOSK82urpP1gkDb3d8Qp5EVkhTuCO8C4ws6PvFIge21e+XgDLgeR2 58 | B6+SPU8yJdZIpYJeqN9eGjlym6J5YwBi4BSGEU8tiXvfg0ZC+zbcj809l70QMtKc 59 | Cb7CFXQcIpfuBHuqQOkN0IphwtYTJ8u+EADFWwbTPqLrshN85BQQ44JNF/BSkl7j 60 | ZnHJwUqMIwliP2xxBfeHBDiSaGkCju1xQh4fRB3ob2UA/W0AAAptuayUkKS1gMVu 61 | e2Y32qzPOY7mwCKahLQ1wn8AB+jVhndHWMgNbDfJ02BtB3oGyvWDuUaS0XYKGncz 62 | 0AE8UNDyn2Xj4uESJFQZ3JP24FVGIDzVUJkYodF4mSZL/KIsjOXSBGitWB7uVlh6 63 | zZzuTkwSbiVvRj75r6xjmTJIlD+JAk4EHwEKADgWIQSA0Vgjt/0VYfn3vN3cMNfC 64 | PLur7gUCXLLmbBcMgAHHT2rJ6TOzBn9S8z+kWexnFbBwXwIHAAAKCRDcMNfCPLur 65 | 7vrPD/9I5p00zJ42MW0wbAEY4QGjiAVRsv1Lw1VUokeT2h6s0sBhYn+SM+lTCAva 66 | Pp7q0KGFjHOSVCIKlweCV/1Iw9EDuReLpfY2eKNFWRDj+lKYSI74Tos73sNHBRvp 67 | 5xXkFqLvNrBmTYfvcqr2FIDfF6LXAZb/yUg6NjE4E93kilwq8lh+3nPqM9apWo9H 68 | 6fr6rGfDt1hlrwUDzrI5O7R5tjjQ1dd79YPYBXS6Sbc3LI8mTH6HIKTVgOw1rsA8 69 | haEL1JwzFiCnbmIZ4s5dc2yc+ALpVc3OdUKrCTpU/AthQAu/RSXGN9AdjdLYPDGY 70 | aFer3pZvN2Nrh1ZB8j+4MY1YiOp0qgLQSxaBqq/JRY7jVDNxMyNADZuf7ji4qeAp 71 | 9nbIiCWjK4oqKKmGG78BxVx05zTteWPtcxkVSsPMfOgjaEefagYLIgv8Be1+avVg 72 | hboLXrOIrHCFPfV7WNeLcLD8Mwz7/JTFP+XobAvim06QSe5u/wJc85AFTKPV+oCx 73 | dn0dE81bp2G9r4/ypROBBEkYnoFN1dhmysXs8c0xRAboK56WxWihVQhiK7fLOonM 74 | zmceMeiaKsQufNoOQ1a3rO4qd4Dks4cwXWiGhWRXSFWY1cCbxP34oo/fFKAxLBdq 75 | RhN/IjafU+tw5SygW/3mkMHKVxJ2Tb+726QPhb/cYfRfpX52+bRHRGViaWFuIEFy 76 | Y2hpdmUgQXV0b21hdGljIFNpZ25pbmcgS2V5ICgxMC9idXN0ZXIpIDxmdHBtYXN0 77 | ZXJAZGViaWFuLm9yZz6JAlQEEwEKAD4WIQSA0Vgjt/0VYfn3vN3cMNfCPLur7gUC 78 | XLLl5wIbAwUJDwmcAAULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRDcMNfCPLur 79 | 7p8KD/4gCYmz6IjMnhsz8x9d5lP3h+wIdUdt0L0QCNceoHcblUFhqx74HwVMLFyY 80 | k+8/WHrLry/N83mgWmP8GOeOsQG0+1Fpd+0ew1+smYagSjyON4crv8W47Yb48qfV 81 | UwT9VRJqdW0zga6KD8F17I3ssOVr9pZTDHa33ykwzg4eUvBs4wYdb5dZMYJImgRA 82 | NRzgeiw70LOMZyaPh6yu7i+qcDuVUP1R8xF14GWmKgczsNnOGvaHTo+lc8SSTwjb 83 | OhkNOSN9X6EYdqXRgyeGGiLcgWL7cOmezLNVOV4pDUD1T0jOXMV/t+2hQaPNmIJO 84 | 2hFa4m8ewi4Yo7QUw9q/NToJNMwtr4ZeFH4taCfHbfIJBQE+BQJ1MXDckH95LFNF 85 | v3Zfh9iwEXyM1P5IgcgGp5mh7Uzs+FfyNLBzIoC09Kgbtrgohihm5S7jJD7ghogW 86 | tQP6Gvz1XWvXOmljv2ccJKezbL82ChED/uSBnWypPxs2zbtyEvX16QnwJsNZMrvT 87 | Whh4/4jaDrM7wncmU4RoV96KwwTlx8V4XlkEielMCt1Po/9Ws3JbdcFKVEIUrLOB 88 | p631evHuUG+mmBlGAX1k8uiEVK3Xvrn3wdDc8+tPSxDQ9GCnQ4YPOv4SU02eUB+q 89 | tBs85NbpULxAweKyMumARNVuqC82viB2YryUZF5+JslFnmb8pokCMwQQAQgAHRYh 90 | BOHPIN3/5LiegCZY8eCxGJT2auyYBQJcsuvcAAoJEOCxGJT2auyYoSMP/ApUnr+O 91 | 6qzfkCNkxWcyFe/cSLsjKYDNeneaGIVnffk1gwltQ6/x3403UYW+HWFMdOf+PzRu 92 | KD0habntmdMZP3a1t0YiJkRF4rGX2rqBegesPiBp74fSlHtuy6cPWlu7PYi0qVs1 93 | uZWiUF3eBo9DhN5j0w0vTaEVBFh1reahhOw5SlTXj2ITGViJXcQtFgcn5CepbZ9q 94 | cswgnCv5RU1qXUxqiOTT/zBmVdOsNiZil5X39L5t8GE6yNCNaQrm+JNM/OWPswEi 95 | fOhN4eiCysIDwKxGLqFvrw3i18iV8zWjJ+sQO2jXeqVFaxfT3HR3S24RO9VpjtIw 96 | s5VdFjhczkqEWAHV/VtERDgrhiEB3tVwrEARNGjuIEJvWEo643KRkI2w+KK7GB0R 97 | p4meBXHhyDucffss/0t5NqZynjZ/DDGWa+bsk/l2BI3KvPi2NZXXCXkZHbDREQka 98 | kjlQgsM8Cy0+a//TU2X+l7+aXHSbrwVlAfF6yA6Lf6yu/GTMyS08rs5pSwxWFucu 99 | cYPgANGD+V6XLn490un7iewcjjml6VKbi0fEqHkUV953tgZtnQGgZ9k3KL7aNdAV 100 | /GtIxc47sL8HEsWgvBOc6s1hXbw7v1+bvI8hS46bhxMYWmXgznAdQPB++Xlc5kHu 101 | QMAyQfaxYui6cXZra6+26sKZv8xYmroQVzk9iQIzBBABCAAdFiEEbtb1y1+m+y9G 102 | CuiO7aDSOIriK6kFAlyy7NwACgkQ7aDSOIriK6mzKhAAhd7CQ/3Bl9Cvk8x+Gt5N 103 | EDnj80gLGKqxUxoRekSAp6Rkh4b7XOBbSb+LHgniPgmXZnnVhNChfAlSmnmS4i+c 104 | hJbu9Y2B987exiNXdBYWE3VBMvzy8a5JbUF8Guqqb9DlzAaD3rHOUSOK3HWi+Rhf 105 | 9wdFKVzDUXku32v4fmxMSSTOqpXRj2iVnuKLCKR18hNiZK5ez434gQDqYDvHuU4/ 106 | jzsXsG4nPKfxvSjZk6hykb0rWvxbmDA1RVTLKAdlL+nm1dNoJKRz7/OmHf/u5Voh 107 | inSDhlXbtWHL1PO7mqgqst5+0qkjImENpsQE9lKAyyV8xo/PsS+pu6N6NPxyjfTL 108 | tHHyBnUOwS09vvib8aVYSH+3GqCz0c0ZpmGaTeDT2fhdCBFs7DKV6HYT3DbnqBnj 109 | tQF2PBFUSDJlbRafDAu2JwLVPC3QL/iYKUn6NQHQkrKPYp8uQAMSLLRCr8lGMCG6 110 | 4oqsMcVXHv3QYrYqQE+83dNSsZa+BabYTyz+tZS9EtJkN65UgrRvRLPvVazAEmJq 111 | uiHZxLuwEuSUmnpSfTY0KGGJMhzsN8AI98K1sqDjrUvmgHH7ACWj0hU3xzkd0yOG 112 | RjH507xOBFNpgN9LsPpRe9h5vpisFOrJYeIp2hQcoPDKHvgdeyFau3qdOItI7S5b 113 | KJUW7UvfXu0pH+HyydTpZX+JAjMEEAEKAB0WIQSA6XbxSlCKSOnKP+m8NyJSyhz5 114 | ZAUCXLLu7gAKCRC8NyJSyhz5ZKgGEACMep8c7JVSEd6hsrmET50hd8U3tlwzhlwj 115 | uNM181mN1P1dV+Tcjprz+Dr3b5U3fuA+Irnijn3Vfvoa/DD5j79dzp8VVO5DlSzx 116 | wTM8fnswlJtSv/NaCAFsErxX7Gi54lgwC1abuUor/YdNimij06hg5PRD8ZtjAM+j 117 | N3OI64vPsmhS+QPD3sz1nuiuh59AXoBcVtND5Ej7nHcK3WOwf8xhvim5g+eyoaPS 118 | T47WzawWjSK/SgBQVeJsU0B0vb+DQGemnd4QyVI5tGKWz+vw0iAXieUksqnIYDlt 119 | NSUgru3I+M0L6cIl9C9oj+gvXn4vSwpuhwpSJZS7ratIrhvY+uShBq0T1gSy1buL 120 | c6hkDvyS+dIqnEZzPfCBAog3Q5mPD0GZ5rzk/XJ9PPTgH4QEug57MvyYyFmvIDtQ 121 | 1ZmfAlxWcKFMCNEpuGhL3DcmZWqd+Fqs4Ik/UsEPQpSVhxcsLf8wDO1dIzJBamlF 122 | 4IJHImoHtsmMFGI9zwNDwBo1jPOKcPt3FbMlQw9KUht/H7Xg6pbRQ6yGVi9ppdiG 123 | k1Eb5B/J72QjwSaVKhC1W/nPNZvF5NxRwImTW1i3Llyy06WebperF7/8Wksk1pHo 124 | GKZHt5JS816DTfOVrsjkFqC66mJCYBy3vEPONJWOo9gohxA7V0SP9vMEZJa8UpaY 125 | rDGyonjq4YkCVQQQAQoAPxYhBPv6vbVBtdyVW9m6btsWz1uxJSXEBQJcs4trIRpo 126 | dHRwOi8vZ3BnLmdhbm5lZmYuZGUvcG9saWN5LnR4dAAKCRDbFs9bsSUlxKm/D/9p 127 | B+G1mLPt2DZveRhLQXi9w0QJlmOH3Ec/KYZKLbrk74yV6hgJS5fP9NYMT5/89wDD 128 | KajmXy30UpiX99Y1nOeSGV7xk0LikiVvv1ZQl3YhsIgyiHiCtYgVXxZPhFYhxHw5 129 | P+7Zdl00gkTilTBuVbaVQLH+S593MBla/IX7PXPZFyPkArh3pyDleiE3AQiU8EWo 130 | 0Zjhntrfa9VQtk79vC1ho0//p+W0EPyhiLl9nzRvxoCjveSMFw8Pn+Qr51FzC/Y+ 131 | EGjYao0H2PLce4CcogWh2no0o1zeFSm8xoyGUgNczs0hMLkrQTkr2+YQj9NJ5oKd 132 | hZM1uRzsJ/DDXaEQTZjj2iIyU8e0E/OhOaq3OnTMVeiZEy5ZvyfyYlkzb5Qmcufv 133 | OCh5rFtUj5+6TGl3ywRyTrs21MjCVwggBn2KU0Kg/gqh2IkPavlV+LecH6CJwplA 134 | lsH1cnnnm2RJwOQhcdAAjbpjvkAVi4k+XJGnVZaeU1KCG8nmVSWdKd60Li4EOPlO 135 | swc5K9GmPFjEfHkY6dynKbzMh8ukSozSF2f7Z0wL+c53jMCHpZ/UZUBqNjmhKcoS 136 | PCME5pKP9rUr+L+sucw9gNC9mwWRTj6KbjLWo7fvQpJaBvcbYNIpKU7ViBe4Blvb 137 | Sl0Me56Cmew4s8G5T2cpUG2Aumg/Rr5lR+MXdfGjVLkCDQRcsuXnARAArgqqMQG0 138 | iABrEdAG6Twzp+wZV7r/2IVqJyhnGyu0+yoOcYqai9eeP8XM3yZk1Y95FE09g7RJ 139 | 2jacyhhC5Tsrg+GVJ/1eSsvudegZn+QnqEZ7HrmwJsYKFKhntak11Tvvhsw08sKM 140 | 4KVoxZSmMgBq84OUW95ILySM9vm8ge1+aYgr70flXhKne+o1VKeHWlovtmIGpWaJ 141 | 7fCHj95pDoJhe6uUkmEIJzMrNIaM7FQ0r4GdBYwqDImW07zMRWk80Av7uf6f+5xc 142 | v27y2yW8ZjKF5u0ZKWln+VZX4EfUdCgJ/0LeV/v9gVbCeanNqGJB6k6DpKu6IzGz 143 | KXi7rHFi1GiuoiVgy9Svx27iRpJaykLxnGFn8C7Lpzo9q034gGIWLwQnjT1FdPya 144 | 2pFV1VHNFZQ3JnQRJwE8yGhw/5bpllaUUJKvydSWvBMgOscEHQdtRnA4IMUXrHGV 145 | IhYN/awYkjhubeVJuhbsxaQDqpdAodaoIz20PVBfE+XFbfnLCBwxgzR/m+mE0iW1 146 | GCOBSoFw5SPQBihCF/PPBjqQjZKJz1btUvrv7gpLNuLEyA0RsHBFGqtqvT1K4Hvx 147 | 6Y7di35/Nm/Jgty2e75vMSGUm1B+G2pFjEypZjtOckOHQ9hVN4svvMJGFnqcwZIa 148 | gMF+67twWmv/AVb5CovsXLKv1qTzplRJWiEAEQEAAYkEcgQYAQoAJhYhBIDRWCO3 149 | /RVh+fe83dww18I8u6vuBQJcsuXnAhsCBQkPCZwAAkAJENww18I8u6vuwXQgBBkB 150 | CgAdFiEEAUbcbUoLKRS97TTbZIrP1iLz0TgFAlyy5ecACgkQZIrP1iLz0TiL/g// 151 | UwdPym98fCTVZJ+HwHId+Ssqo6vTgxA/6DLGRvFILie40vA4OnFrozusDVh/x+Vv 152 | +pxbtdw3w16kfpDifKicx2o4ZyEYl30pdVuBmSEOhFvI3ZgN6P79/Dv3KhD3QQPK 153 | OMSxXO2vCh7BebmpfT2rdukgFED9vxbj1Ec7IMfm4VobFJZaFXZKsTBc09MQU2Bm 154 | 1JvtzINsdwzp/sFTilxmqO7kX4DmTM3k1KYmMkx7xq5KUaxSORZHIqDcIy74pOIw 155 | TuvHN98cYujCKFDk0MfHBovXPUnFHFxd+OgSEbxGnb4Uuus1h89VIU5xviQHPGe0 156 | T9qG6tUBvFuCkPzcWxUg4AN6nxZz8stZHhd0ceuSDeYnGBk6X/eEcYmy/kEbJEqj 157 | f+kuY4VFIDkShnnDrKchyoi/LmkfvW4fOEtTpmB8nkflolKfVaN2dEo2hyma3iKC 158 | 5zp8n8hlNwhkt3DiGyYXU0RD7JAbX4jVZSVov5PhAjmrEksxslv/ICrAJ7zfCx62 159 | zzm37TGwiQJTWQsIcQ2PRPWFWk/CHAVjNPsu2QpMsGUWccGUOI6a70LsVnnufLzt 160 | c73TM37Jv9hCXljRvVRikTy+StjFZlVQdXoZvNJhhIE/W+/iNoBvChD8pKSWe6RJ 161 | Yto5CxCQtN6IKgAiUtoXusAgFSB7TZ5CJF1NFZ0VQabJcw/9GunyNNj+RRdMXbHI 162 | VbrDQoqKY1FAhIUE0cURfkVE7z0mYUUZ5bwILchQsvwVsQKorVmryh1fgaYCOi+H 163 | 4kvmhljN9HqB9I7vgRaYAJ3qwgYIUselclYN4SNniHzatRMROppUMs9W5ytENGhx 164 | oPARiZpRVL+rPPaFdip33c27pVdNAU/lRq2ZpzkdSTv+2V9GmVfDtcKv9A4uDqJ9 165 | 7ttgZCaifNbHShzMEWRCXSsT7/52XB7KlxmAynwPNMLeM+/0JTCLyFBEvyejvgCM 166 | GqgvMDEddarHhd6ChdXLJLBAeXVBGRygWcDBO5rX8GPMb0y5/yE+UVprkx3jSb2m 167 | sl9nUW2UcOhfrtu+CPS3qazu6h/QkTwitzAFSn57DtGmwKLzqk63g9TgcjBg1HtZ 168 | S66DzdsJ4Y6Iy51oNyHx3EBLzmdFfxKAeABsapvJl7fhiC93CC3hZTKUyBjr6Dru 169 | I2wktWCAAMHFE0eeyIreCHdzzMtu+V2H+X9GJMxzd5jOYBI3vy946R2jG5gX+WyD 170 | calvWyo8N+XrZKD8NQnWQ/BocU9r5S5aJFcovdcmm1s1Ymdlo5Yuk8WHZDOsSf38 171 | VzY12szoQ9eMbBJOH7MhseS/gIWC/4x1eEEhGbPQbkzKZlJifv+55Mqqq7emGyBG 172 | qn8+ouVQUr65+xcIST13Ffg80zc= 173 | =5Cty 174 | -----END PGP PUBLIC KEY BLOCK----- 175 | -------------------------------------------------------------------------------- /debian/mkimage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Enable tracing if TRACE environment variable is set 4 | if [ -n "$TRACE" ]; then 5 | export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' 6 | set -o xtrace 7 | fi 8 | 9 | # Exit on error and pipefail 10 | set -o errexit 11 | set -o pipefail 12 | 13 | # Set non-interactive frontend for Debian packages 14 | export DEBIAN_FRONTEND=noninteractive 15 | 16 | # Get the directory of the script 17 | scriptdir=$(dirname "${BASH_SOURCE[0]}") 18 | 19 | # Function to display usage information 20 | usage() { 21 | cat < Specify the name of the output image file. 31 | --release= Specify the Debian release (e.g., bullseye, buster). 32 | --keyring= Specify the keyring package for debootstrap. 33 | --variant= Specify the debootstrap variant (e.g., minbase). 34 | --repo_config= Specify the repository configuration file. 35 | --debootstrap_packages= Comma-separated list of packages for debootstrap. 36 | --packages= Comma-separated list of additional packages to install. 37 | --recipes= Comma-separated list of installer scripts to run. 38 | --scripts= Comma-separated list of test scripts to execute. 39 | --help Display this help message. 40 | 41 | Example: 42 | ${0} --name=my-image --release=bullseye --packages=curl,vim --scripts=test.sh 43 | 44 | Notes: 45 | - Ensure that debootstrap, unzip, and trivy are installed on your system. 46 | - The script requires root privileges for certain operations. 47 | EOF 48 | exit 1 49 | } 50 | 51 | # Parse command-line arguments 52 | OPTIND=1 53 | while getopts ":-:" optchar; do 54 | [[ "${optchar}" == "-" ]] || continue 55 | case "${OPTARG}" in 56 | name=*) 57 | name=${OPTARG#*=} 58 | if [[ -z "$name" ]]; then 59 | die "Missing value for --name" 60 | fi 61 | ;; 62 | release=*) 63 | release=${OPTARG#*=} 64 | if [[ -z "$release" ]]; then 65 | die "Missing value for --release" 66 | fi 67 | ;; 68 | keyring=*) 69 | keyring=${OPTARG#*=} 70 | if [[ -z "$keyring" ]]; then 71 | die "Missing value for --keyring" 72 | fi 73 | ;; 74 | variant=*) 75 | variant=${OPTARG#*=} 76 | ;; 77 | repo_config=*) 78 | repo_config=${OPTARG#*=} 79 | ;; 80 | debootstrap_packages=*) 81 | debootstrap_packages=${OPTARG#*=} 82 | ;; 83 | packages=*) 84 | packages=${OPTARG#*=} 85 | packages=("${packages//,/ }") 86 | ;; 87 | recipes=*) 88 | recipes=${OPTARG#*=} 89 | recipes=("${recipes//,/ }") 90 | ;; 91 | scripts=*) 92 | scripts=${OPTARG#*=} 93 | scripts=("${scripts//,/ }") 94 | ;; 95 | help*) 96 | usage 97 | ;; 98 | *) 99 | echo "Unknown argument: '$OPTARG'. Did you mean one of these?" 100 | echo " --name=" 101 | echo " --release=" 102 | echo " --keyring=" 103 | echo "Run ${0} --help for more information." 104 | exit 1 105 | ;; 106 | esac 107 | done 108 | shift $((OPTIND - 1)) 109 | 110 | ############################## FUNCTIONS ############################## 111 | 112 | # Check if debootstrap is installed 113 | if ! command -v debootstrap >/dev/null 2>&1; then 114 | echo "Error: debootstrap is not installed." 115 | echo "To install debootstrap, run:" 116 | echo " sudo apt-get update && sudo apt-get install debootstrap" 117 | exit 1 118 | fi 119 | 120 | # Check if unzip is installed 121 | if ! command -v unzip >/dev/null 2>&1; then 122 | echo "Error: unzip is not installed." 123 | echo "To install unzip, run:" 124 | echo " sudo apt-get update && sudo apt-get install unzip" 125 | exit 1 126 | fi 127 | 128 | # Check if trivy is installed 129 | if ! command -v trivy >/dev/null 2>&1; then 130 | echo "Error: trivy is not installed." 131 | echo "Trivy is a security scanner used to detect vulnerabilities, misconfigurations, and secrets." 132 | echo "To install trivy, follow these steps:" 133 | echo "" 134 | echo "For Linux/macOS:" 135 | echo " Download the latest release from https://github.com/aquasecurity/trivy/releases" 136 | echo " Example for Linux:" 137 | echo " curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin" 138 | echo "" 139 | exit 1 140 | fi 141 | 142 | # Function to run commands with error handling 143 | run() { 144 | echo >&2 "$(timestamp) RUN $* " 145 | "${@}" 146 | e=$? 147 | 148 | if ((e != 0)); then 149 | echo >&2 "Error code: $e, program was interrupted" 150 | exit 151 | fi 152 | } 153 | 154 | # Function to print headers 155 | header() { 156 | echo 157 | msg="${1:-}" 158 | echo 159 | printf ["$msg"] >/dev/stderr 160 | echo 161 | printf ':%.0s' $(seq 1 80) 162 | echo 163 | } 164 | 165 | # Function to generate a timestamp 166 | timestamp() { 167 | date +"[%Y-%m-%d %T] -" 168 | } 169 | 170 | # Function to exit with an error message 171 | die() { 172 | echo "ERROR $*" >&2 173 | exit 1 174 | } 175 | 176 | # Function to print warnings 177 | warn() { 178 | echo >&2 "$(timestamp) WARN $*" 179 | } 180 | 181 | # Function to print informational messages 182 | info() { 183 | echo >&2 "$(timestamp) INFO $*" 184 | } 185 | 186 | # Function to source all files in a directory 187 | source-files-in() { 188 | local dir="$1" 189 | 190 | if [[ -d "$dir" && -r "$dir" && -x "$dir" ]]; then 191 | for file in "$dir"/*; do 192 | [[ -f "$file" && -r "$file" ]] && run source "$file" 193 | done 194 | fi 195 | } 196 | 197 | # Function to print array elements 198 | print-array() { 199 | printf '%s\n' "${@}" 200 | } 201 | 202 | # Function to print logs 203 | printlog() { 204 | printf '\n' 205 | printf '%s\n' "${*}" 206 | printf '\n' 207 | } 208 | 209 | # Function to check if required commands are installed 210 | check-commands() { 211 | local commands=("${@}") 212 | 213 | for cmd in $(print-array "${commands[@]}"); do 214 | echo "Checking command $cmd" 215 | if ! command -v "$cmd" &>/dev/null; then 216 | die "$cmd not found" 217 | fi 218 | done 219 | } 220 | 221 | # Function to start a timer 222 | timer-on() { 223 | start=$(date +%s) 224 | } 225 | 226 | # Function to stop a timer and display elapsed time 227 | timer-off() { 228 | end=$(date +%s) 229 | elapsed=$((end - start)) 230 | echo "Time elapsed: $elapsed" 231 | echo 232 | } 233 | 234 | # Function to get the real path of a file 235 | frealpath() { 236 | [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" 237 | } 238 | 239 | # Function to retry a command 240 | retry() { 241 | local tries=$1 242 | shift 243 | 244 | local i=0 245 | while [ "$i" -lt "$tries" ]; do 246 | "$@" && return 0 247 | sleep $((2**((i++)))) 248 | done 249 | 250 | return 1 251 | } 252 | 253 | # Function to check file checksum 254 | check_sum() { 255 | local sum=$1 256 | local file=$2 257 | local sha=$3 258 | 259 | info "File: $file" 260 | info "Checking: $sum" 261 | 262 | if [[ $sha == "sha512sum" ]]; then 263 | sha=sha512sum 264 | else 265 | sha=sha256sum 266 | fi 267 | 268 | ${sha} --check - <> "$target"/etc/apt/sources.list 356 | echo "deb $sec_repo_url $release-security main" >> "$target"/etc/apt/sources.list 357 | run chroot "$target" apt-get update && apt-get install --yes --option Dpkg::Options::="--force-confdef" 358 | 359 | if [[ -v packages[@] ]]; then 360 | header "Installing packages" 361 | info "The following packages will be installed in chroot:" 362 | print-array ${packages[@]} 363 | echo 364 | run chroot "$target" apt-get update && retry 3 chroot "$target" apt-get install --yes --no-install-recommends "${packages[@]}" 365 | info "Installed packages:" 366 | chroot "$target" dpkg-query --show --showformat='${Package} ${Installed-Size}\n' 367 | fi 368 | 369 | if [[ -v recipes[@] ]]; then 370 | header "Running installer scripts" 371 | while read -r line; do 372 | info "Running ${line}" 373 | run source "${line}" && `basename ${line} .sh` 374 | done < <(print-array ${recipes[@]}) 375 | fi 376 | 377 | header "Apply Docker-specific apt settings" 378 | run chroot "$target" apt-get --option Acquire::Check-Valid-Until=false update 379 | run chroot "$target" apt-get --yes --quiet upgrade 380 | echo '#!/bin/sh' > "$target"/usr/sbin/policy-rc.d 381 | echo 'exit 101' >> "$target"/usr/sbin/policy-rc.d 382 | run chmod +x "$target"/usr/sbin/policy-rc.d 383 | run dpkg-divert --local --rename --add "$target"/sbin/initctl 384 | run cp --archive "$target"/usr/sbin/policy-rc.d "$target"/sbin/initctl 385 | run sed --in-place 's/^exit.*/exit 0/' "$target"/sbin/initctl 386 | echo 'force-unsafe-io' > "$target"/etc/dpkg/dpkg.cfg.d/docker-apt-speedup 387 | echo 'DPkg::Post-Invoke { "rm --force /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' > "$target"/etc/apt/apt.conf.d/docker-clean 388 | echo 'APT::Update::Post-Invoke { "rm --force /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' >> "$target"/etc/apt/apt.conf.d/docker-clean 389 | echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";' >> "$target"/etc/apt/apt.conf.d/docker-clean 390 | echo 'Acquire::Languages "none";' > "$target"/etc/apt/apt.conf.d/docker-no-languages 391 | echo 'Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";' > "$target"/etc/apt/apt.conf.d/docker-gzip-indexes 392 | echo 'Apt::AutoRemove::SuggestsImportant "false";' > "$target"/etc/apt/apt.conf.d/docker-autoremove-suggests 393 | 394 | header "Remove optional files to reduce the size of the directory" 395 | run chroot "$target" apt-get clean 396 | run chroot "$target" apt-get --yes autoremove 397 | run rm --recursive --force "$target"/dev "$target"/proc 398 | run mkdir --parents "$target"/dev "$target"/proc 399 | run rm --recursive --force "$target"/usr/bin/pinky 400 | run rm --recursive --force "$target"/etc/apt/apt.conf.d/01autoremove-kernels 401 | run rm --recursive --force "$target"/etc/machine-id 402 | run rm --recursive --force "$target"/etc/boot 403 | run rm --recursive --force "$target"/etc/hostname 404 | run rm --recursive --force "$target"/tmp/* "$target"/var/tmp/* 405 | run rm --recursive --force "$target"/etc/systemd/* "$target"/lib/systemd/* 406 | run rm --recursive --force "$target"/var/lib/apt/lists/* 407 | run rm --recursive --force "$target"/usr/share/info/* 408 | run rm --recursive --force "$target"/usr/lib/x86_64-linux-gnu/gconv/IBM* "$target"/usr/lib/x86_64-linux-gnu/gconv/EBC* 409 | run rm --recursive --force "$target"/usr/share/groff 410 | run rm --recursive --force "$target"/usr/share/lintian 411 | run rm --recursive --force "$target"/usr/share/linda 412 | run rm --recursive --force "$target"/var/lib/apt/lists/* 413 | run rm --recursive --force "$target"/usr/share/doc/* 414 | run rm --recursive --force "$target"/usr/share/pixmaps/* 415 | run rm --recursive --force "$target"/usr/share/locale/* 416 | run find "$target"/var/cache -type f -exec rm --recursive --force {} \; 417 | run find "$target"/var/log -type f -exec truncate --size 0 {} \; 418 | run rm --recursive --force "$target"/etc/ld.so.cache && run chroot "$target" ldconfig 419 | 420 | if [[ -v scripts[@] ]]; then 421 | header "Running tests" 422 | source "${scripts}" 423 | fi 424 | 425 | header "Archiving image" 426 | GZIP="--no-name" run tar --numeric-owner -czf "$dist"/"$name".tar --directory "$target" . --transform='s,^./,,' --mtime='1970-01-01' 427 | md5sum "$dist"/"$name".tar > "$dist"/"$name".SUM 428 | 429 | header "Remove temporary directories" 430 | run rm --recursive --force "$target" 431 | run rm --recursive --force "$debootstrap_dir" 432 | 433 | header "Image was built successfully" 434 | echo 435 | echo "Artifact location: "$dist"/"$name".tar" 436 | echo 437 | echo "Artifact size: `du --summarize --human-readable "$dist"/"$name".tar | cut --fields 1`" 438 | echo 439 | echo "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" 440 | echo "Image was built successfully" 441 | echo "Artifact location: "$dist"/"$name".tar" 442 | echo "" 443 | echo "To load and run this Docker image, follow these steps:" 444 | echo "" 445 | echo "Load the Docker image from the .tar file:" 446 | echo " docker import "$dist"/"$name".tar "$name"" 447 | echo "" 448 | echo "Verify the image was loaded successfully:" 449 | echo " docker images" 450 | echo "" 451 | echo "Run the Docker container:" 452 | echo " docker run -it " 453 | echo " Replace with the name of the image loaded in the first step." 454 | echo "" 455 | echo "Example:" 456 | echo " docker run -it "$name" /bin/bash" 457 | echo "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" 458 | 459 | timer-off 460 | } 461 | 462 | main "${@}" 2>&1 | tee "$dist"/"$logfile".log 463 | -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 51 | 52 | 53 | 54 | 55 | 56 | 61 | 66 | 67 | 68 | 73 | 74 | 75 | 80 | 81 | 82 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /recipes/java/corretto.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CORRETO_VERSION='17.0.9.8.1' 4 | CORRETO_SHA='0cf11d8e41d7b28a3dbb95cbdd90c398c310a9ea870e5a06dac65a004612aa62' 5 | CORRETO_URL="https://corretto.aws/downloads/resources/${CORRETO_VERSION}/amazon-corretto-${CORRETO_VERSION}-linux-x64.tar.gz" 6 | 7 | JDK_VERSION=${CORRETO_VERSION} 8 | JDK_SHA=${CORRETO_SHA} 9 | JDK_URL=${CORRETO_URL} 10 | 11 | corretto() { 12 | if [[ ! -f ${DOWNLOAD}/jdk-${JDK_VERSION}.tar.gz ]]; then 13 | download ${JDK_URL} amazon-corretto-${JDK_VERSION}.tar.gz ${DOWNLOAD} 14 | fi 15 | check_sum ${JDK_SHA} ${DOWNLOAD}/amazon-corretto-${JDK_VERSION}.tar.gz 16 | run tar -xzf ${DOWNLOAD}/amazon-corretto-${JDK_VERSION}.tar.gz --directory "$target"/opt 17 | run mv "$target"/opt/amazon-corretto-${JDK_VERSION}-linux-x64 "$target"/opt/jdk/ 18 | 19 | run find "$target"/opt/jdk/bin -type f ! -path "./*"/java-rmi.cgi -exec strip --strip-all {} \; 20 | run find "$target"/opt/jdk -name "*.so*" -exec strip --strip-all {} \; 21 | run find "$target"/opt/jdk -name jexec -exec strip --strip-all {} \; 22 | run find "$target"/opt/jdk -name "*.debuginfo" -exec rm --force {} \; 23 | run find "$target"/opt/jdk -name "*src*zip" -exec rm --force {} \; 24 | 25 | run rm --recursive --force "$target"/opt/jdk/bin/appletviewer 26 | run rm --recursive --force "$target"/opt/jdk/bin/extcheck 27 | run rm --recursive --force "$target"/opt/jdk/bin/idlj 28 | run rm --recursive --force "$target"/opt/jdk/bin/jarsigner 29 | run rm --recursive --force "$target"/opt/jdk/bin/javah 30 | run rm --recursive --force "$target"/opt/jdk/bin/javap 31 | run rm --recursive --force "$target"/opt/jdk/bin/jconsole 32 | run rm --recursive --force "$target"/opt/jdk/bin/jdmpview 33 | run rm --recursive --force "$target"/opt/jdk/bin/jdb 34 | run rm --recursive --force "$target"/opt/jdk/bin/jhat 35 | run rm --recursive --force "$target"/opt/jdk/bin/jjs 36 | run rm --recursive --force "$target"/opt/jdk/bin/jmap 37 | run rm --recursive --force "$target"/opt/jdk/bin/jrunscript 38 | run rm --recursive --force "$target"/opt/jdk/bin/jstack 39 | run rm --recursive --force "$target"/opt/jdk/bin/jstat 40 | run rm --recursive --force "$target"/opt/jdk/bin/jstatd 41 | run rm --recursive --force "$target"/opt/jdk/bin/native2ascii 42 | run rm --recursive --force "$target"/opt/jdk/bin/orbd 43 | run rm --recursive --force "$target"/opt/jdk/bin/policytool 44 | run rm --recursive --force "$target"/opt/jdk/bin/rmic 45 | run rm --recursive --force "$target"/opt/jdk/bin/tnameserv 46 | run rm --recursive --force "$target"/opt/jdk/bin/schemagen 47 | run rm --recursive --force "$target"/opt/jdk/bin/serialver 48 | run rm --recursive --force "$target"/opt/jdk/bin/servertool 49 | run rm --recursive --force "$target"/opt/jdk/bin/tnameserv 50 | run rm --recursive --force "$target"/opt/jdk/bin/traceformat 51 | run rm --recursive --force "$target"/opt/jdk/bin/wsgen 52 | run rm --recursive --force "$target"/opt/jdk/bin/wsimport 53 | run rm --recursive --force "$target"/opt/jdk/bin/xjc 54 | 55 | run rm --recursive --force "$target"/opt/jdk/jmods/java.activation.jmod 56 | run rm --recursive --force "$target"/opt/jdk/jmods/java.corba.jmod 57 | run rm --recursive --force "$target"/opt/jdk/jmods/java.transaction.jmod 58 | run rm --recursive --force "$target"/opt/jdk/jmods/java.xml.ws.jmod 59 | run rm --recursive --force "$target"/opt/jdk/jmods/java.xml.ws.annotation.jmod 60 | run rm --recursive --force "$target"/opt/jdk/jmods/java.desktop.jmod 61 | run rm --recursive --force "$target"/opt/jdk/jmods/java.datatransfer.jmod 62 | run rm --recursive --force "$target"/opt/jdk/jmods/jdk.scripting.nashorn.jmod 63 | run rm --recursive --force "$target"/opt/jdk/jmods/jdk.scripting.nashorn.shell.jmod 64 | run rm --recursive --force "$target"/opt/jdk/jmods/jdk.jconsole.jmod 65 | run rm --recursive --force "$target"/opt/jdk/jmods/java.scripting.jmod 66 | run rm --recursive --force "$target"/opt/jdk/jmods/java.se.ee.jmod 67 | run rm --recursive --force "$target"/opt/jdk/jmods/java.se.jmod 68 | run rm --recursive --force "$target"/opt/jdk/jmods/java.sql.jmod 69 | run rm --recursive --force "$target"/opt/jdk/jmods/java.sql.rowset.jmod 70 | 71 | run rm --recursive --force "$target"/opt/jdk/lib/jexec 72 | 73 | # https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/ 74 | echo -e '\n### JAVA ###' >> "$target"/root/.bashrc 75 | echo 'export JAVA_HOME=/opt/jdk' >> "$target"/root/.bashrc 76 | echo 'export CLASSPATH=.:$JAVA_HOME/lib/' >> "$target"/root/.bashrc 77 | echo 'export PATH=$JAVA_HOME/bin:$PATH' >> "$target"/root/.bashrc 78 | cat "$target"/root/.bashrc 79 | } 80 | -------------------------------------------------------------------------------- /recipes/java/graalvm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GRAALVM_VERSION='20.0.2' 4 | GRAALVM_SHA='941a85a690e7b1c4e1fcfac321561ca46033bba3ac4882dd15d4f45edd06726c' 5 | GRAALVM_URL="https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${GRAALVM_VERSION}/graalvm-community-jdk-${GRAALVM_VERSION}_linux-x64_bin.tar.gz" 6 | 7 | graalvm() { 8 | if [[ ! -f ${DOWNLOAD}/graal-${GRAALVM_VERSION}.tar.gz ]]; then 9 | download ${GRAALVM_URL} graal-${GRAALVM_VERSION}.tar.gz ${DOWNLOAD} 10 | fi 11 | check_sum ${GRAALVM_SHA} ${DOWNLOAD}/graal-${GRAALVM_VERSION}.tar.gz 12 | run tar -xzf ${DOWNLOAD}/graal-${GRAALVM_VERSION}.tar.gz --directory "$target"/opt 13 | run mv "$target"/opt/graalvm-community-openjdk-${GRAALVM_VERSION}+9.1 "$target"/opt/graal/ 14 | 15 | run find "$target"/opt/graal/bin -type f ! -path "./*"/java-rmi.cgi -exec strip --strip-all {} \; 16 | run find "$target"/opt/graal -name "*.so*" -exec strip --strip-all {} \; 17 | run find "$target"/opt/graal -name jexec -exec strip --strip-all {} \; 18 | run find "$target"/opt/graal -name "*.debuginfo" -exec rm --force {} \; 19 | run find "$target"/opt/graal -name "*src*zip" -exec rm --force {} \; 20 | 21 | run rm --recursive --force "$target"/opt/graal/bin/appletviewer 22 | run rm --recursive --force "$target"/opt/graal/bin/extcheck 23 | run rm --recursive --force "$target"/opt/graal/bin/idlj 24 | run rm --recursive --force "$target"/opt/graal/bin/jarsigner 25 | run rm --recursive --force "$target"/opt/graal/bin/javah 26 | run rm --recursive --force "$target"/opt/graal/bin/javap 27 | run rm --recursive --force "$target"/opt/graal/bin/jconsole 28 | run rm --recursive --force "$target"/opt/graal/bin/jdmpview 29 | run rm --recursive --force "$target"/opt/graal/bin/jdb 30 | run rm --recursive --force "$target"/opt/graal/bin/jhat 31 | run rm --recursive --force "$target"/opt/graal/bin/jjs 32 | run rm --recursive --force "$target"/opt/graal/bin/jmap 33 | run rm --recursive --force "$target"/opt/graal/bin/jrunscript 34 | run rm --recursive --force "$target"/opt/graal/bin/jstack 35 | run rm --recursive --force "$target"/opt/graal/bin/jstat 36 | run rm --recursive --force "$target"/opt/graal/bin/jstatd 37 | run rm --recursive --force "$target"/opt/graal/bin/native2ascii 38 | run rm --recursive --force "$target"/opt/graal/bin/orbd 39 | run rm --recursive --force "$target"/opt/graal/bin/policytool 40 | run rm --recursive --force "$target"/opt/graal/bin/rmic 41 | run rm --recursive --force "$target"/opt/graal/bin/tnameserv 42 | run rm --recursive --force "$target"/opt/graal/bin/schemagen 43 | run rm --recursive --force "$target"/opt/graal/bin/serialver 44 | run rm --recursive --force "$target"/opt/graal/bin/servertool 45 | run rm --recursive --force "$target"/opt/graal/bin/tnameserv 46 | run rm --recursive --force "$target"/opt/graal/bin/traceformat 47 | run rm --recursive --force "$target"/opt/graal/bin/wsgen 48 | run rm --recursive --force "$target"/opt/graal/bin/wsimport 49 | run rm --recursive --force "$target"/opt/graal/bin/xjc 50 | 51 | run rm --recursive --force "$target"/opt/graal/jmods/java.activation.jmod 52 | run rm --recursive --force "$target"/opt/graal/jmods/java.corba.jmod 53 | run rm --recursive --force "$target"/opt/graal/jmods/java.transaction.jmod 54 | run rm --recursive --force "$target"/opt/graal/jmods/java.xml.ws.jmod 55 | run rm --recursive --force "$target"/opt/graal/jmods/java.xml.ws.annotation.jmod 56 | run rm --recursive --force "$target"/opt/graal/jmods/java.desktop.jmod 57 | run rm --recursive --force "$target"/opt/graal/jmods/java.datatransfer.jmod 58 | run rm --recursive --force "$target"/opt/graal/jmods/jdk.scripting.nashorn.jmod 59 | run rm --recursive --force "$target"/opt/graal/jmods/jdk.scripting.nashorn.shell.jmod 60 | run rm --recursive --force "$target"/opt/graal/jmods/jdk.jconsole.jmod 61 | run rm --recursive --force "$target"/opt/graal/jmods/java.scripting.jmod 62 | run rm --recursive --force "$target"/opt/graal/jmods/java.se.ee.jmod 63 | run rm --recursive --force "$target"/opt/graal/jmods/java.se.jmod 64 | run rm --recursive --force "$target"/opt/graal/jmods/java.sql.jmod 65 | run rm --recursive --force "$target"/opt/graal/jmods/java.sql.rowset.jmod 66 | 67 | run rm --recursive --force "$target"/opt/graal/lib/jexec 68 | 69 | echo -e '\n### GRAAL ###' >> "$target"/root/.bashrc 70 | echo 'export GRAALVM_HOME=/opt/graal' >> "$target"/root/.bashrc 71 | echo 'export JAVA_HOME=$GRAALVM_HOME' >> "$target"/root/.bashrc 72 | echo 'export PATH=$GRAALVM_HOME/bin:$PATH' >> "$target"/root/.bashrc 73 | cat "$target"/root/.bashrc 74 | } -------------------------------------------------------------------------------- /recipes/java/graalvm_slim.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GRAALVM_VERSION='20.0.2' 4 | GRAALVM_SHA='941a85a690e7b1c4e1fcfac321561ca46033bba3ac4882dd15d4f45edd06726c' 5 | GRAALVM_URL="https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${GRAALVM_VERSION}/graalvm-community-jdk-${GRAALVM_VERSION}_linux-x64_bin.tar.gz" 6 | 7 | graalvm_slim() { 8 | if [[ ! -f ${DOWNLOAD}/graal-${GRAALVM_VERSION}.tar.gz ]]; then 9 | download ${GRAALVM_URL} graal-${GRAALVM_VERSION}.tar.gz ${DOWNLOAD} 10 | fi 11 | check_sum ${GRAALVM_SHA} ${DOWNLOAD}/graal-${GRAALVM_VERSION}.tar.gz 12 | run tar -xzf ${DOWNLOAD}/graal-${GRAALVM_VERSION}.tar.gz --directory "$target"/tmp 13 | run mkdir --parents "$target"/tmp/graal 14 | run mv "$target"/tmp/graalvm-community-openjdk-${GRAALVM_VERSION}+9.1/* "$target"/tmp/graal 15 | 16 | # https://www.oracle.com/corporate/features/understanding-java-9-modules.html 17 | # https://docs.oracle.com/javase/9/tools/jlink.htm 18 | run "$target"/tmp/graal/bin/jlink \ 19 | --add-modules ALL-MODULE-PATH \ 20 | --strip-debug \ 21 | --no-man-pages \ 22 | --no-header-files \ 23 | --compress=2 \ 24 | --vm=server \ 25 | --output "$target"/opt/graal 26 | 27 | run rm --recursive --force "$target"/tmp/* 28 | 29 | echo -e '\n### GRAAL ###' >> "$target"/root/.bashrc 30 | echo 'export GRAALVM_HOME=/opt/graal' >> "$target"/root/.bashrc 31 | echo 'export JAVA_HOME=$GRAALVM_HOME' >> "$target"/root/.bashrc 32 | echo 'export PATH=$GRAALVM_HOME/bin:$PATH' >> "$target"/root/.bashrc 33 | cat "$target"/root/.bashrc 34 | } -------------------------------------------------------------------------------- /recipes/java/gradle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GRADLE_VERSION='7.4.2' 4 | GRADLE_SHA='29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda' 5 | GRADLE_URL="https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" 6 | 7 | gradle() { 8 | if [[ ! -f ${DOWNLOAD}/gradle-${GRADLE_VERSION}-bin.zip ]]; then 9 | download ${GRADLE_URL} gradle-${GRADLE_VERSION}-bin.zip ${DOWNLOAD} 10 | fi 11 | check_sum ${GRADLE_SHA} ${DOWNLOAD}/gradle-${GRADLE_VERSION}-bin.zip 12 | run unzip ${DOWNLOAD}/gradle-${GRADLE_VERSION}-bin.zip -d "$target"/opt 13 | run mv "$target"/opt/gradle-${GRADLE_VERSION} "$target"/opt/gradle 14 | 15 | # https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_environment_variables 16 | echo -e '\n ### GRADLE ###' >> "$target"/root/.bashrc 17 | echo 'export GRADLE_HOME=/opt/gradle' >> "$target"/root/.bashrc 18 | echo 'export PATH=$PATH:$GRADLE_HOME/bin:$PATH' >> "$target"/root/.bashrc 19 | echo 'export GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.logging.level=info"' >> "$target"/root/.bashrc 20 | cat "$target"/root/.bashrc 21 | } 22 | -------------------------------------------------------------------------------- /recipes/java/java.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | JDK_VERSION='21.0.1' 4 | JDK_SHA='7e80146b2c3f719bf7f56992eb268ad466f8854d5d6ae11805784608e458343f' 5 | JDK_URL="https://download.java.net/java/GA/jdk${JDK_VERSION}/415e3f918a1f4062a0074a2794853d0d/12/GPL/openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz" 6 | 7 | java() { 8 | if [[ ! -f ${DOWNLOAD}/jdk-${JDK_VERSION}.tar.gz ]]; then 9 | download ${JDK_URL} jdk-${JDK_VERSION}.tar.gz ${DOWNLOAD} 10 | fi 11 | check_sum ${JDK_SHA} ${DOWNLOAD}/jdk-${JDK_VERSION}.tar.gz 12 | run tar -xzf ${DOWNLOAD}/jdk-${JDK_VERSION}.tar.gz --directory "$target"/tmp 13 | run mkdir --parents "$target"/opt/jdk 14 | run mv "$target"/tmp/jdk-${JDK_VERSION}/* "$target"/opt/jdk 15 | 16 | run find "$target"/opt/jdk/bin -type f ! -path "./*"/java-rmi.cgi -exec strip --strip-all {} \; 17 | run find "$target"/opt/jdk -name "*.so*" -exec strip --strip-all {} \; 18 | run find "$target"/opt/jdk -name jexec -exec strip --strip-all {} \; 19 | run find "$target"/opt/jdk -name "*.debuginfo" -exec rm --force {} \; 20 | run find "$target"/opt/jdk -name "*src*zip" -exec rm --force {} \; 21 | 22 | run rm --recursive --force "$target"/opt/jdk/bin/appletviewer 23 | run rm --recursive --force "$target"/opt/jdk/bin/extcheck 24 | run rm --recursive --force "$target"/opt/jdk/bin/idlj 25 | run rm --recursive --force "$target"/opt/jdk/bin/jarsigner 26 | run rm --recursive --force "$target"/opt/jdk/bin/javah 27 | run rm --recursive --force "$target"/opt/jdk/bin/javap 28 | run rm --recursive --force "$target"/opt/jdk/bin/jconsole 29 | run rm --recursive --force "$target"/opt/jdk/bin/jdmpview 30 | run rm --recursive --force "$target"/opt/jdk/bin/jdb 31 | run rm --recursive --force "$target"/opt/jdk/bin/jhat 32 | run rm --recursive --force "$target"/opt/jdk/bin/jjs 33 | run rm --recursive --force "$target"/opt/jdk/bin/jmap 34 | run rm --recursive --force "$target"/opt/jdk/bin/jrunscript 35 | run rm --recursive --force "$target"/opt/jdk/bin/jstack 36 | run rm --recursive --force "$target"/opt/jdk/bin/jstat 37 | run rm --recursive --force "$target"/opt/jdk/bin/jstatd 38 | run rm --recursive --force "$target"/opt/jdk/bin/native2ascii 39 | run rm --recursive --force "$target"/opt/jdk/bin/orbd 40 | run rm --recursive --force "$target"/opt/jdk/bin/policytool 41 | run rm --recursive --force "$target"/opt/jdk/bin/rmic 42 | run rm --recursive --force "$target"/opt/jdk/bin/tnameserv 43 | run rm --recursive --force "$target"/opt/jdk/bin/schemagen 44 | run rm --recursive --force "$target"/opt/jdk/bin/serialver 45 | run rm --recursive --force "$target"/opt/jdk/bin/servertool 46 | run rm --recursive --force "$target"/opt/jdk/bin/tnameserv 47 | run rm --recursive --force "$target"/opt/jdk/bin/traceformat 48 | run rm --recursive --force "$target"/opt/jdk/bin/wsgen 49 | run rm --recursive --force "$target"/opt/jdk/bin/wsimport 50 | run rm --recursive --force "$target"/opt/jdk/bin/xjc 51 | 52 | run rm --recursive --force "$target"/opt/jdk/jmods/java.activation.jmod 53 | run rm --recursive --force "$target"/opt/jdk/jmods/java.corba.jmod 54 | run rm --recursive --force "$target"/opt/jdk/jmods/java.transaction.jmod 55 | run rm --recursive --force "$target"/opt/jdk/jmods/java.xml.ws.jmod 56 | run rm --recursive --force "$target"/opt/jdk/jmods/java.xml.ws.annotation.jmod 57 | run rm --recursive --force "$target"/opt/jdk/jmods/java.desktop.jmod 58 | run rm --recursive --force "$target"/opt/jdk/jmods/java.datatransfer.jmod 59 | run rm --recursive --force "$target"/opt/jdk/jmods/jdk.scripting.nashorn.jmod 60 | run rm --recursive --force "$target"/opt/jdk/jmods/jdk.scripting.nashorn.shell.jmod 61 | run rm --recursive --force "$target"/opt/jdk/jmods/jdk.jconsole.jmod 62 | run rm --recursive --force "$target"/opt/jdk/jmods/java.scripting.jmod 63 | run rm --recursive --force "$target"/opt/jdk/jmods/java.se.ee.jmod 64 | run rm --recursive --force "$target"/opt/jdk/jmods/java.se.jmod 65 | run rm --recursive --force "$target"/opt/jdk/jmods/java.sql.jmod 66 | run rm --recursive --force "$target"/opt/jdk/jmods/java.sql.rowset.jmod 67 | 68 | run rm --recursive --force "$target"/opt/jdk/lib/jexec 69 | 70 | # https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/ 71 | echo -e '\n### JAVA ###' >> "$target"/root/.bashrc 72 | echo 'export JAVA_HOME=/opt/jdk' >> "$target"/root/.bashrc 73 | echo 'export CLASSPATH=.:$JAVA_HOME/lib/' >> "$target"/root/.bashrc 74 | echo 'export PATH=$JAVA_HOME/bin:$PATH' >> "$target"/root/.bashrc 75 | cat "$target"/root/.bashrc 76 | } 77 | -------------------------------------------------------------------------------- /recipes/java/java_slim.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | JDK_VERSION='21.0.1' 4 | JDK_SHA='7e80146b2c3f719bf7f56992eb268ad466f8854d5d6ae11805784608e458343f' 5 | JDK_URL="https://download.java.net/java/GA/jdk${JDK_VERSION}/415e3f918a1f4062a0074a2794853d0d/12/GPL/openjdk-${JDK_VERSION}_linux-x64_bin.tar.gz" 6 | 7 | java_slim() { 8 | if [[ ! -f ${DOWNLOAD}/jdk-${JDK_VERSION}.tar.gz ]]; then 9 | download ${JDK_URL} jdk-${JDK_VERSION}.tar.gz ${DOWNLOAD} 10 | fi 11 | check_sum ${JDK_SHA} ${DOWNLOAD}/jdk-${JDK_VERSION}.tar.gz 12 | run tar -xzf ${DOWNLOAD}/jdk-${JDK_VERSION}.tar.gz --directory "$target"/tmp 13 | run mkdir --parents "$target"/tmp/jdk 14 | run mv "$target"/tmp/jdk-${JDK_VERSION}/* "$target"/tmp/jdk 15 | 16 | # https://www.oracle.com/corporate/features/understanding-java-9-modules.html 17 | # https://docs.oracle.com/javase/9/tools/jlink.htm 18 | run "$target"/tmp/jdk/bin/jlink \ 19 | --add-modules ALL-MODULE-PATH \ 20 | --strip-java-debug-attributes \ 21 | --no-man-pages \ 22 | --no-header-files \ 23 | --compress=2 \ 24 | --vm=server \ 25 | --output "$target"/opt/jdk 26 | 27 | run rm --recursive --force "$target"/tmp/* 28 | 29 | # https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/ 30 | echo -e '\n### JAVA ###' >> "$target"/root/.bashrc 31 | echo 'export JAVA_HOME=/opt/jdk' >> "$target"/root/.bashrc 32 | echo 'export CLASSPATH=.:$JAVA_HOME/lib/' >> "$target"/root/.bashrc 33 | echo 'export PATH=$JAVA_HOME/bin:$PATH' >> "$target"/root/.bashrc 34 | cat "$target"/root/.bashrc 35 | } 36 | -------------------------------------------------------------------------------- /recipes/java/maven.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | MAVEN_VERSION='3.8.8' 4 | MAVEN_SHA='332088670d14fa9ff346e6858ca0acca304666596fec86eea89253bd496d3c90deae2be5091be199f48e09d46cec817c6419d5161fb4ee37871503f472765d00' 5 | MAVEN_URL="https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz" 6 | 7 | maven() { 8 | if [[ ! -f ${DOWNLOAD}/maven-${MAVEN_VERSION}.tar.gz ]]; then 9 | download ${MAVEN_URL} maven-${MAVEN_VERSION}.tar.gz ${DOWNLOAD} 10 | fi 11 | check_sum ${MAVEN_SHA} ${DOWNLOAD}/maven-${MAVEN_VERSION}.tar.gz sha512sum 12 | run mkdir --parents "$target"/opt/maven 13 | run tar -xzf ${DOWNLOAD}/maven-${MAVEN_VERSION}.tar.gz --strip-components=1 --directory "$target"/opt/maven 14 | 15 | # https://maven.apache.org/configure.html 16 | echo -e '\n### MAVEN ###' >> "$target"/root/.bashrc 17 | echo 'export MAVEN_HOME=/opt/maven' >> "$target"/root/.bashrc 18 | echo 'export PATH=$PATH:$MAVEN_HOME/bin:$PATH' >> "$target"/root/.bashrc 19 | echo 'export MAVEN_OPTS="-Xms256m -Xmx1024m"' >> "$target"/root/.bashrc 20 | } -------------------------------------------------------------------------------- /recipes/kafka/kafka.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | KAFKA_VERSION='4.0.0' 4 | KAFKA_SHA='7b852e938bc09de10cd96eca3755258c7d25fb89dbdd76305717607e1835e2aa' 5 | KAFKA_URL="https://downloads.apache.org/kafka/${KAFKA_VERSION}/kafka_2.13-${KAFKA_VERSION}.tgz" 6 | 7 | kafka() { 8 | # Check if Java is installed (required for Kafka) 9 | if [[ ! -d "$target/opt/jdk" && ! -d "$target/opt/graal" ]]; then 10 | error 1 JAVA_REQUIRED "Kafka requires Java to be installed. Please include a Java recipe first." 11 | fi 12 | 13 | # Download Kafka 14 | if [[ ! -f ${DOWNLOAD}/kafka-${KAFKA_VERSION}.tgz ]]; then 15 | download ${KAFKA_URL} kafka-${KAFKA_VERSION}.tgz ${DOWNLOAD} 16 | fi 17 | 18 | # Verify checksum 19 | check_sum ${KAFKA_SHA} ${DOWNLOAD}/kafka-${KAFKA_VERSION}.tgz 20 | 21 | # Install Kafka 22 | run mkdir --parents "$target"/opt/kafka 23 | run tar -xzf ${DOWNLOAD}/kafka-${KAFKA_VERSION}.tgz --strip-components=1 --directory "$target"/opt/kafka 24 | 25 | # Create directories for Kafka data 26 | run mkdir --parents "$target"/var/lib/kafka/data 27 | run mkdir --parents "$target"/var/log/kafka 28 | 29 | # Configure Kafka environment 30 | echo -e '\n### KAFKA ###' >> "$target"/root/.bashrc 31 | echo 'export KAFKA_HOME=/opt/kafka' >> "$target"/root/.bashrc 32 | echo 'export PATH=$PATH:$KAFKA_HOME/bin' >> "$target"/root/.bashrc 33 | 34 | # Create a basic Kafka configuration file 35 | cat > "$target"/opt/kafka/config/server.properties < "$target"/opt/kafka/bin/start-kafka.sh < "$target"/etc/systemd/system/kafka.service <> "$target"/etc/profile 50 | echo 'export NODE_HOME=/opt/nodejs' >> "$target"/etc/profile 51 | } 52 | -------------------------------------------------------------------------------- /recipes/php/php.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # PHP version and download details 4 | PHP_VERSION='8.4.6' 5 | PHP_SHA='089b08a5efef02313483325f3bacd8c4fe311cf1e1e56749d5cc7d059e225631' 6 | PHP_URL="https://www.php.net/distributions/php-${PHP_VERSION}.tar.gz" 7 | 8 | 9 | php() { 10 | header "Installing build dependencies" 11 | local build_deps=( 12 | build-essential 13 | pkg-config 14 | libxml2-dev 15 | libsqlite3-dev 16 | libcurl4-openssl-dev 17 | libonig-dev 18 | libssl-dev 19 | zlib1g-dev 20 | libzip-dev 21 | libreadline-dev 22 | ) 23 | run apt-get update 24 | run apt-get install -y --no-install-recommends "${build_deps[@]}" 25 | 26 | header "Downloading PHP ${PHP_VERSION}" 27 | if [[ ! -f "${DOWNLOAD}/php-${PHP_VERSION}.tar.xz" ]]; then 28 | download "${PHP_URL}" "php-${PHP_VERSION}.tar.xz" "${DOWNLOAD}" || 29 | die "Failed to download PHP" 30 | fi 31 | check_sum "${PHP_SHA}" "${DOWNLOAD}/php-${PHP_VERSION}.tar.xz" || 32 | die "Checksum verification failed" 33 | 34 | header "Building PHP ${PHP_VERSION}" 35 | run mkdir -p "${DOWNLOAD}/php-build" 36 | run tar -xJf "${DOWNLOAD}/php-${PHP_VERSION}.tar.xz" -C "${DOWNLOAD}/php-build" --strip-components=1 37 | 38 | pushd "${DOWNLOAD}/php-build" >/dev/null 39 | 40 | # Configure PHP with common production settings 41 | ./configure \ 42 | --prefix=/usr \ 43 | --with-config-file-path=/etc/php \ 44 | --with-config-file-scan-dir=/etc/php/conf.d \ 45 | --enable-fpm \ 46 | --with-fpm-user=www-data \ 47 | --with-fpm-group=www-data \ 48 | --enable-mbstring \ 49 | --enable-zip \ 50 | --enable-bcmath \ 51 | --enable-pcntl \ 52 | --enable-ftp \ 53 | --enable-exif \ 54 | --enable-calendar \ 55 | --with-openssl \ 56 | --with-curl \ 57 | --with-zlib \ 58 | --with-zip \ 59 | --with-pear \ 60 | --with-readline \ 61 | --enable-opcache \ 62 | --without-sqlite3 \ 63 | --with-pdo-sqlite \ 64 | --enable-mysqlnd \ 65 | --without-pdo-sqlite 66 | 67 | # Build with limited parallelism 68 | run make -j$(($(nproc)/2)) || run make 69 | 70 | # Install into target 71 | run make install DESTDIR="$target" 72 | popd >/dev/null 73 | 74 | header "Configuring PHP" 75 | # Create directory structure 76 | run mkdir -p "$target/etc/php/conf.d" 77 | run mkdir -p "$target/var/log/php" 78 | run mkdir -p "$target/var/run/php" 79 | 80 | # Create default php.ini files 81 | cat > "$target/etc/php/php.ini" < "$target/etc/php/php-fpm.conf" <> "$target"/root/.bashrc 263 | echo 'export PATH=/usr/bin:$PATH' >> "$target"/root/.bashrc 264 | 265 | # Create symlinks 266 | run ln -sf /usr/bin/php "$target"/usr/local/bin/php 267 | run ln -sf /usr/sbin/php-fpm "$target"/usr/local/sbin/php-fpm 268 | 269 | header "Cleaning up" 270 | run apt-get purge -y "${build_deps[@]}" 271 | run apt-get autoremove -y 272 | run apt-get clean 273 | run rm -rf "${DOWNLOAD}/php-build" /var/lib/apt/lists/* 274 | 275 | header "Verifying installation" 276 | if [ -f "$target/usr/bin/php" ]; then 277 | info "PHP found at /usr/bin/php" 278 | chroot "$target" /usr/bin/php --version 279 | else 280 | die "PHP installation failed - binary not found" 281 | fi 282 | 283 | info "PHP ${PHP_VERSION} installed successfully" 284 | } 285 | -------------------------------------------------------------------------------- /recipes/python/python.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Python version information 4 | PYTHON_VERSION='3.9.18' 5 | PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f1-2) 6 | PYTHON_SHA='01597db0132c1cf7b331eff68ae09b5a235a3c3caa9c944c29cac7d1c4c4c00a' 7 | PYTHON_URL="https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz" 8 | 9 | python() { 10 | header "Installing build dependencies" 11 | local build_deps=( 12 | build-essential 13 | zlib1g-dev 14 | libssl-dev 15 | libreadline-dev 16 | libffi-dev 17 | libbz2-dev 18 | libsqlite3-dev 19 | liblzma-dev 20 | tk-dev 21 | ) 22 | run apt-get update 23 | run apt-get install -y --no-install-recommends "${build_deps[@]}" 24 | 25 | header "Downloading Python ${PYTHON_VERSION}" 26 | if [[ ! -f "${DOWNLOAD}/Python-${PYTHON_VERSION}.tar.xz" ]]; then 27 | download "${PYTHON_URL}" "Python-${PYTHON_VERSION}.tar.xz" "${DOWNLOAD}" || 28 | die "Failed to download Python" 29 | fi 30 | check_sum "${PYTHON_SHA}" "${DOWNLOAD}/Python-${PYTHON_VERSION}.tar.xz" || 31 | die "Checksum verification failed" 32 | 33 | header "Building Python ${PYTHON_VERSION}" 34 | run mkdir -p "${DOWNLOAD}/python-build" 35 | run tar -xJf "${DOWNLOAD}/Python-${PYTHON_VERSION}.tar.xz" -C "${DOWNLOAD}/python-build" --strip-components=1 36 | 37 | pushd "${DOWNLOAD}/python-build" >/dev/null 38 | 39 | # Fix for modern systems 40 | run curl -o config.sub 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' 41 | run curl -o config.guess 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' 42 | run chmod +x config.sub config.guess 43 | 44 | # Configure with optimizations 45 | ./configure \ 46 | --prefix=/usr \ 47 | --enable-optimizations \ 48 | --enable-shared \ 49 | --with-system-ffi \ 50 | --with-ensurepip=install \ 51 | LDFLAGS="-Wl,-rpath=/usr/lib" 52 | 53 | # Build with limited parallelism 54 | run make -j$(($(nproc)/2)) || run make 55 | 56 | # Install into target 57 | run make install DESTDIR="$target" 58 | popd >/dev/null 59 | 60 | header "Creating symlinks" 61 | run ln -sf "/usr/bin/python${PYTHON_MINOR}" "$target/usr/bin/python3" 62 | run ln -sf "/usr/bin/python${PYTHON_MINOR}" "$target/usr/bin/python" 63 | run ln -sf "/usr/bin/pip${PYTHON_MINOR}" "$target/usr/bin/pip3" 64 | run ln -sf "/usr/bin/pip${PYTHON_MINOR}" "$target/usr/bin/pip" 65 | 66 | header "Cleaning up" 67 | run apt-get purge -y "${build_deps[@]}" 68 | run apt-get autoremove -y 69 | run apt-get clean 70 | run rm -rf "${DOWNLOAD}/python-build" /var/lib/apt/lists/* 71 | 72 | header "Verifying installation" 73 | if [ -f "$target/usr/bin/python${PYTHON_MINOR}" ]; then 74 | info "Python found at /usr/bin/python${PYTHON_MINOR}" 75 | else 76 | die "Python installation failed - binary not found" 77 | fi 78 | 79 | info "Python ${PYTHON_VERSION} installed successfully" 80 | } 81 | -------------------------------------------------------------------------------- /scripts/cosign.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | import argparse 6 | import logging 7 | from pathlib import Path 8 | 9 | # Configure logging 10 | logging.basicConfig( 11 | level=logging.INFO, 12 | format="%(asctime)s [%(levelname)s] %(message)s", 13 | handlers=[logging.StreamHandler()], 14 | ) 15 | logger = logging.getLogger(__name__) 16 | 17 | def run_command(command, cwd=None, dry_run=False): 18 | """Run a shell command and return its output.""" 19 | try: 20 | logger.debug(f"Running command: {command}") 21 | if dry_run: 22 | logger.info(f"[Dry Run] Skipping execution of: {command}") 23 | return "" 24 | result = subprocess.run( 25 | command, 26 | shell=True, 27 | check=True, 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | stderr=subprocess.PIPE, 31 | cwd=cwd, 32 | ) 33 | return result.stdout.strip() 34 | except subprocess.CalledProcessError as e: 35 | logger.error(f"Command failed: {e.stderr}") 36 | raise 37 | 38 | def find_tar_files(directory): 39 | """Find all .tar files in the specified directory.""" 40 | tar_files = list(Path(directory).rglob("*.tar")) 41 | if not tar_files: 42 | logger.warning(f"No .tar files found in directory: {directory}") 43 | return tar_files 44 | 45 | def is_docker_archive(tar_file): 46 | """Check if the tar file is a Docker archive (created with docker save).""" 47 | try: 48 | contents = run_command(f"tar tf {tar_file}") 49 | return 'manifest.json' in contents or 'repositories' in contents 50 | except Exception: 51 | return False 52 | 53 | def sign_image(tar_file, key=None, registry=None, dry_run=False): 54 | """Sign a tar archive image using cosign.""" 55 | try: 56 | image_name = Path(tar_file).stem 57 | temp_tag = f"temp_{image_name}:latest" 58 | final_tag = f"{registry}/{image_name}:latest" if registry else f"{image_name}:latest" 59 | 60 | # Determine if this is a Docker archive or filesystem tarball 61 | if is_docker_archive(tar_file): 62 | logger.info(f"Loading Docker archive: {tar_file}") 63 | run_command(f"docker load -i {tar_file}", dry_run=dry_run) 64 | 65 | # Get the loaded image tag 66 | load_output = run_command(f"docker load -i {tar_file}", dry_run=dry_run) 67 | loaded_tag = None 68 | for line in load_output.split('\n'): 69 | if 'Loaded image:' in line: 70 | loaded_tag = line.split('Loaded image:')[-1].strip() 71 | break 72 | 73 | if not loaded_tag: 74 | raise Exception("Could not determine loaded image tag") 75 | 76 | # Tag the image with our desired name 77 | logger.info(f"Tagging image: {loaded_tag} as: {final_tag}") 78 | run_command(f"docker tag {loaded_tag} {final_tag}", dry_run=dry_run) 79 | else: 80 | logger.info(f"Importing filesystem tarball: {tar_file} as {final_tag}") 81 | run_command(f"docker import {tar_file} {final_tag}", dry_run=dry_run) 82 | 83 | # Push to registry if specified 84 | if registry: 85 | logger.info(f"Pushing image: {final_tag}") 86 | run_command(f"docker push {final_tag}", dry_run=dry_run) 87 | 88 | # Get the image digest 89 | logger.info(f"Getting digest for image: {final_tag}") 90 | inspect_output = run_command(f"docker inspect {final_tag} --format='{{{{.RepoDigests}}}}'", dry_run=dry_run) 91 | if not inspect_output or inspect_output == '[]': 92 | # If no digest available (local image not pushed), create one 93 | if registry: 94 | raise Exception("Image was not properly pushed to registry") 95 | else: 96 | # For local images, we'll use the image ID 97 | inspect_output = run_command(f"docker inspect {final_tag} --format='{{{{.Id}}}}'", dry_run=dry_run) 98 | image_reference = inspect_output.strip() 99 | else: 100 | # Extract the digest from the RepoDigests output 101 | image_reference = inspect_output.strip("[]'").split('@')[-1] 102 | 103 | # Sign the image using cosign with the digest 104 | logger.info(f"Signing image digest: {image_reference}") 105 | if key: 106 | sign_cmd = f"cosign sign --key {key} {image_reference}" 107 | else: 108 | logger.warning("Using keyless signing. Ensure COSIGN_EXPERIMENTAL=1 is set.") 109 | sign_cmd = f"cosign sign {image_reference}" 110 | 111 | run_command(sign_cmd, dry_run=dry_run) 112 | 113 | logger.info(f"Successfully signed image: {image_reference}") 114 | 115 | except Exception as e: 116 | logger.error(f"Failed to sign image: {tar_file}. Error: {e}") 117 | raise 118 | 119 | def main(): 120 | parser = argparse.ArgumentParser(description="Sign tar archive images using cosign.") 121 | parser.add_argument( 122 | "--directory", 123 | default="dist/", 124 | help="Directory containing tar archives to sign (default: dist/).", 125 | ) 126 | parser.add_argument( 127 | "--key", help="Private key for signing (optional, defaults to cosign's keyless mode)." 128 | ) 129 | parser.add_argument( 130 | "--registry", help="Push signed images to a container registry (optional)." 131 | ) 132 | parser.add_argument( 133 | "--dry-run", 134 | action="store_true", 135 | help="Perform a dry run without executing commands.", 136 | ) 137 | 138 | # Parse arguments 139 | args = parser.parse_args() 140 | 141 | # Show help if no arguments are provided 142 | if len(sys.argv) == 1: # No arguments provided 143 | parser.print_help() 144 | return 145 | 146 | # Check if cosign is installed 147 | try: 148 | run_command("cosign version", dry_run=args.dry_run) 149 | except Exception: 150 | logger.error( 151 | "Cosign is not installed. To install cosign, visit https://docs.sigstore.dev/cosign/installation/" 152 | ) 153 | exit(1) 154 | 155 | # Find all .tar files in the specified directory 156 | tar_files = find_tar_files(args.directory) 157 | if not tar_files: 158 | logger.info("No .tar files found. Exiting.") 159 | return 160 | 161 | # Sign each tar file sequentially 162 | for tar_file in tar_files: 163 | sign_image(tar_file, args.key, args.registry, args.dry_run) 164 | 165 | logger.info("All images have been processed successfully.") 166 | 167 | if __name__ == "__main__": 168 | import sys # Import sys to check argument length 169 | main() 170 | -------------------------------------------------------------------------------- /scripts/gpg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | import argparse 6 | import logging 7 | from pathlib import Path 8 | 9 | # Configure logging 10 | logging.basicConfig( 11 | level=logging.INFO, 12 | format="%(asctime)s [%(levelname)s] %(message)s", 13 | handlers=[logging.StreamHandler()], 14 | ) 15 | logger = logging.getLogger(__name__) 16 | 17 | def run_command(command, cwd=None, dry_run=False): 18 | """Run a shell command and return its output.""" 19 | try: 20 | logger.debug(f"Running command: {command}") 21 | if dry_run: 22 | logger.info(f"[Dry Run] Skipping execution of: {command}") 23 | return "" 24 | result = subprocess.run( 25 | command, 26 | shell=True, 27 | check=True, 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | stderr=subprocess.PIPE, 31 | cwd=cwd, 32 | ) 33 | logger.debug(f"Command output: {result.stdout.strip()}") 34 | return result.stdout.strip() 35 | except subprocess.CalledProcessError as e: 36 | logger.error(f"Command failed: {e.stderr}") 37 | raise 38 | 39 | def find_tar_files(directory): 40 | """Find all .tar files in the specified directory.""" 41 | tar_files = list(Path(directory).rglob("*.tar")) 42 | if not tar_files: 43 | logger.warning(f"No .tar files found in directory: {directory}") 44 | return tar_files 45 | 46 | def is_valid_tar_file(tar_file): 47 | """Check if the file is a valid tar archive.""" 48 | try: 49 | # Use `tar tf` to list contents; if it fails, it's not a valid tar file 50 | run_command(f"tar tf {tar_file}") 51 | return True 52 | except Exception: 53 | logger.warning(f"File is not a valid tar archive: {tar_file}") 54 | return False 55 | 56 | def sign_tarball_with_gpg(tar_file, gpg_key_id, passphrase=None, dry_run=False): 57 | """Sign a tarball using GPG.""" 58 | try: 59 | signature_file = f"{tar_file}.asc" # Default to .asc for ASCII-armored signatures 60 | logger.info(f"Signing tarball: {tar_file} -> Signature: {signature_file}") 61 | 62 | # Check if the signature file already exists 63 | if Path(signature_file).exists(): 64 | overwrite = input(f"File '{signature_file}' exists. Overwrite? (y/N) ").strip().lower() 65 | if overwrite != "y": 66 | logger.info(f"Skipping signing of {tar_file}.") 67 | return 68 | 69 | # Construct the GPG signing command 70 | gpg_cmd = ["gpg", "--detach-sign", "--armor"] 71 | if gpg_key_id: 72 | gpg_cmd.extend(["--local-user", gpg_key_id]) 73 | if passphrase: 74 | gpg_cmd.extend(["--batch", "--pinentry-mode", "loopback", "--passphrase", passphrase]) 75 | 76 | gpg_cmd.append(str(tar_file)) 77 | 78 | # Execute the GPG command 79 | if dry_run: 80 | logger.info(f"[Dry Run] Skipping execution of: {' '.join(gpg_cmd)}") 81 | else: 82 | result = subprocess.run( 83 | gpg_cmd, 84 | check=True, 85 | text=True, 86 | stdout=subprocess.PIPE, 87 | stderr=subprocess.PIPE, 88 | ) 89 | if result.returncode != 0: 90 | raise Exception(f"GPG signing failed: {result.stderr}") 91 | 92 | logger.info(f"Successfully signed tarball: {tar_file}") 93 | logger.info(f"Signature saved to: {signature_file}") 94 | 95 | except Exception as e: 96 | logger.error(f"Failed to sign tarball: {tar_file}. Error: {e}") 97 | raise 98 | 99 | def verify_tarball_with_gpg(tar_file, sig_file, dry_run=False): 100 | """Verify the signature of a tarball using GPG.""" 101 | try: 102 | if not Path(sig_file).exists(): 103 | logger.warning(f"Signature file not found: {sig_file}") 104 | return False 105 | 106 | logger.info(f"Verifying tarball: {tar_file} against signature: {sig_file}") 107 | 108 | # Construct the GPG verification command 109 | gpg_cmd = ["gpg", "--verify", str(sig_file), str(tar_file)] 110 | 111 | # Execute the GPG command 112 | if dry_run: 113 | logger.info(f"[Dry Run] Skipping execution of: {' '.join(gpg_cmd)}") 114 | else: 115 | result = subprocess.run( 116 | gpg_cmd, 117 | check=True, 118 | text=True, 119 | stdout=subprocess.PIPE, 120 | stderr=subprocess.PIPE, 121 | ) 122 | if result.returncode == 0: 123 | logger.info(f"Verification successful: {tar_file}") 124 | return True 125 | else: 126 | logger.error(f"Verification failed: {tar_file}") 127 | return False 128 | 129 | except Exception as e: 130 | logger.error(f"Failed to verify tarball: {tar_file}. Error: {e}") 131 | return False 132 | 133 | def main(): 134 | parser = argparse.ArgumentParser(description="Sign or verify tarball archives using GPG.") 135 | parser.add_argument( 136 | "--directory", 137 | default="dist/", 138 | help="Directory or file containing tar archives to process (default: dist/).", 139 | ) 140 | parser.add_argument( 141 | "--gpg-key-id", 142 | help="GPG key ID to use for signing (required for signing).", 143 | ) 144 | parser.add_argument( 145 | "--passphrase", 146 | help="Passphrase for the GPG key (optional, will prompt if not provided).", 147 | ) 148 | parser.add_argument( 149 | "--sig-file", 150 | help="Path to the signature file (.asc or .sig) for verification (optional).", 151 | ) 152 | parser.add_argument( 153 | "--verify", 154 | action="store_true", 155 | help="Verify signatures instead of signing.", 156 | ) 157 | parser.add_argument( 158 | "--dry-run", 159 | action="store_true", 160 | help="Perform a dry run without executing commands.", 161 | ) 162 | 163 | # Parse arguments 164 | args = parser.parse_args() 165 | 166 | # Show help if no arguments are provided 167 | if len(sys.argv) == 1: # No arguments provided 168 | parser.print_help() 169 | return 170 | 171 | # Check if GPG is installed 172 | try: 173 | run_command("gpg --version", dry_run=args.dry_run) 174 | except Exception: 175 | logger.error( 176 | "GPG is not installed. To install GPG, visit https://gnupg.org/download/" 177 | ) 178 | exit(1) 179 | 180 | # Determine if the input is a file or directory 181 | path = Path(args.directory) 182 | if path.is_file() and path.suffix == ".tar": 183 | tar_files = [path] # Treat as a single file 184 | elif path.is_dir(): 185 | tar_files = find_tar_files(path) # Search for .tar files in the directory 186 | else: 187 | logger.error(f"Invalid input: {args.directory} is neither a .tar file nor a directory.") 188 | exit(1) 189 | 190 | if not tar_files: 191 | logger.info("No .tar files found. Exiting.") 192 | return 193 | 194 | # Process each tar file sequentially 195 | for tar_file in tar_files: 196 | if not is_valid_tar_file(tar_file): 197 | logger.warning(f"Skipping invalid tar file: {tar_file}") 198 | continue 199 | 200 | if args.verify: 201 | # Use the provided signature file or default to .asc 202 | sig_file = args.sig_file or f"{tar_file}.asc" 203 | if not Path(sig_file).exists(): 204 | logger.warning(f"Signature file not found: {sig_file}. Skipping verification.") 205 | continue 206 | verify_tarball_with_gpg(tar_file, sig_file, dry_run=args.dry_run) 207 | else: 208 | if not args.gpg_key_id: 209 | logger.error("GPG key ID (--gpg-key-id) is required for signing.") 210 | exit(1) 211 | sign_tarball_with_gpg( 212 | tar_file, 213 | gpg_key_id=args.gpg_key_id, 214 | passphrase=args.passphrase, 215 | dry_run=args.dry_run, 216 | ) 217 | 218 | logger.info("All tarballs have been processed successfully.") 219 | 220 | if __name__ == "__main__": 221 | import sys # Import sys to check argument length 222 | main() 223 | -------------------------------------------------------------------------------- /scripts/security-scan.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | info "Scanning with trivy" 4 | run trivy fs --no-progress "$target" 2>&1 | tee ${dist}/security_scan.txt 5 | -------------------------------------------------------------------------------- /scripts/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | import argparse 6 | import logging 7 | from pathlib import Path 8 | 9 | # Configure logging 10 | logging.basicConfig( 11 | level=logging.INFO, 12 | format="%(asctime)s [%(levelname)s] %(message)s", 13 | handlers=[logging.StreamHandler()], 14 | ) 15 | logger = logging.getLogger(__name__) 16 | 17 | def run_command(command, cwd=None, dry_run=False): 18 | """Run a shell command and return its output.""" 19 | try: 20 | logger.debug(f"Running command: {command}") 21 | if dry_run: 22 | logger.info(f"[Dry Run] Skipping execution of: {command}") 23 | return "", "" 24 | result = subprocess.run( 25 | command, 26 | shell=True, 27 | check=False, # Allow non-zero exit codes to capture stderr 28 | text=True, 29 | stdout=subprocess.PIPE, 30 | stderr=subprocess.PIPE, 31 | cwd=cwd, 32 | ) 33 | logger.debug(f"Command stdout: {result.stdout.strip()}") 34 | logger.debug(f"Command stderr: {result.stderr.strip()}") 35 | return result.stdout.strip(), result.stderr.strip() 36 | except Exception as e: 37 | logger.error(f"Command failed: {e}") 38 | raise 39 | 40 | def validate_image(image_id): 41 | """Check if the Docker image exists locally.""" 42 | try: 43 | # Use `docker inspect` to check if the image exists 44 | run_command(f"docker inspect --type=image {image_id}") 45 | return True 46 | except Exception: 47 | logger.warning(f"Docker image not found: {image_id}") 48 | return False 49 | 50 | def validate_config_file(config_file): 51 | """Check if the YAML config file exists and has a valid extension.""" 52 | if not config_file.exists(): 53 | logger.error(f"Config file not found: {config_file}") 54 | return False 55 | if config_file.suffix not in (".yaml", ".yml"): 56 | logger.error(f"Invalid file type: {config_file}. Must be a .yaml or .yml file.") 57 | return False 58 | return True 59 | 60 | def run_container_test(image_id, config_file, dry_run=False): 61 | """Run container-structure-test for the given image and config file.""" 62 | try: 63 | logger.info(f"Testing image: {image_id} with config: {config_file}") 64 | 65 | # Construct the container-structure-test command 66 | test_cmd = [ 67 | "container-structure-test", 68 | "test", 69 | "--image", image_id, 70 | "--config", str(config_file), 71 | ] 72 | 73 | # Execute the command 74 | if dry_run: 75 | logger.info(f"[Dry Run] Skipping execution of: {' '.join(test_cmd)}") 76 | return 77 | 78 | stdout, stderr = run_command(" ".join(test_cmd), dry_run=dry_run) 79 | 80 | # Log the output 81 | if stdout: 82 | logger.info(f"Container Structure Test Output:\n{stdout}") 83 | if stderr: 84 | logger.error(f"Container Structure Test Errors:\n{stderr}") 85 | 86 | # Check the exit code 87 | result = subprocess.run( 88 | " ".join(test_cmd), 89 | shell=True, 90 | check=False, 91 | text=True, 92 | stdout=subprocess.PIPE, 93 | stderr=subprocess.PIPE, 94 | ) 95 | if result.returncode == 0: 96 | logger.info(f"Tests passed for image: {image_id} with config: {config_file}") 97 | else: 98 | logger.error(f"Tests failed for image: {image_id} with config: {config_file}") 99 | 100 | except Exception as e: 101 | logger.error(f"Failed to run tests for image: {image_id}. Error: {e}") 102 | raise 103 | 104 | def main(): 105 | parser = argparse.ArgumentParser(description="Run container-structure-tests for Docker images.") 106 | parser.add_argument( 107 | "--image", 108 | required=True, 109 | help="Docker image ID or tag to test.", 110 | ) 111 | parser.add_argument( 112 | "--config", 113 | required=True, 114 | help="Path to a single YAML config file.", 115 | ) 116 | parser.add_argument( 117 | "--dry-run", 118 | action="store_true", 119 | help="Perform a dry run without executing commands.", 120 | ) 121 | 122 | # Parse arguments 123 | args = parser.parse_args() 124 | 125 | # Show help if no arguments are provided 126 | if len(sys.argv) == 1: # No arguments provided 127 | parser.print_help() 128 | return 129 | 130 | # Check if container-structure-test is installed 131 | try: 132 | run_command("container-structure-test version", dry_run=args.dry_run) 133 | except Exception: 134 | logger.error( 135 | "container-structure-test is not installed. To install, visit https://github.com/GoogleContainerTools/container-structure-test" 136 | ) 137 | exit(1) 138 | 139 | # Validate the Docker image 140 | if not validate_image(args.image): 141 | logger.error(f"Docker image not found: {args.image}") 142 | exit(1) 143 | 144 | # Validate the YAML config file 145 | config_file = Path(args.config) 146 | if not validate_config_file(config_file): 147 | exit(1) 148 | 149 | # Run the test 150 | run_container_test(args.image, config_file, dry_run=args.dry_run) 151 | 152 | logger.info("All tests have been processed successfully.") 153 | 154 | if __name__ == "__main__": 155 | import sys # Import sys to check argument length 156 | main() 157 | -------------------------------------------------------------------------------- /test/debian11-nodejs-23.11.0.yaml: -------------------------------------------------------------------------------- 1 | schemaVersion: '2.0.0' 2 | commandTests: 3 | - name: 'node-version' 4 | command: 'node' 5 | args: ['--version'] 6 | expectedOutput: ['v23.11.0'] 7 | 8 | - name: 'npm-version' 9 | command: 'npm' 10 | args: ['--version'] 11 | expectedOutput: ['10.9.2'] 12 | 13 | - name: 'npx-version' 14 | command: 'npx' 15 | args: ['--version'] 16 | expectedOutput: ['10.9.2'] 17 | 18 | fileExistenceTests: 19 | - name: 'Node Binary' 20 | path: '/opt/nodejs/bin/node' 21 | shouldExist: true 22 | isExecutableBy: 'owner' 23 | 24 | - name: 'NPM Binary' 25 | path: '/opt/nodejs/bin/npm' 26 | shouldExist: true 27 | isExecutableBy: 'owner' 28 | 29 | - name: 'NPX Binary' 30 | path: '/opt/nodejs/bin/npx' 31 | shouldExist: true 32 | isExecutableBy: 'owner' 33 | 34 | - name: 'Symlink for Node' 35 | path: '/usr/bin/node' 36 | shouldExist: true 37 | isExecutableBy: 'owner' 38 | 39 | - name: 'Symlink for NPM' 40 | path: '/usr/bin/npm' 41 | shouldExist: true 42 | isExecutableBy: 'owner' 43 | 44 | - name: 'Symlink for NPX' 45 | path: '/usr/bin/npx' 46 | shouldExist: true 47 | isExecutableBy: 'owner' 48 | 49 | licenseTests: 50 | - debian: false 51 | files: 52 | - "/opt/nodejs/LICENSE" 53 | - "/opt/nodejs/README.md" 54 | 55 | --------------------------------------------------------------------------------