From be8045891865cb900f0256b9bdce5a85694671c7 Mon Sep 17 00:00:00 2001 From: mrkmntal Date: Thu, 27 Nov 2025 14:04:03 -0500 Subject: [PATCH] New simplified Chromebook centric branch, without volume mounting on home + new alpine container image (should work on any aarch64) --- app.py | 76 +++++++++++++++----------- fastfetch_config.jsonc | 15 +++++ micro-alpine-dev/Dockerfile | 53 ++++++++++++++++++ micro-alpine-dev/build-dockerfile.sh | 8 +++ micro-alpine-dev/fastfetch_config.json | 15 +++++ 5 files changed, 135 insertions(+), 32 deletions(-) create mode 100644 fastfetch_config.jsonc create mode 100644 micro-alpine-dev/Dockerfile create mode 100755 micro-alpine-dev/build-dockerfile.sh create mode 100644 micro-alpine-dev/fastfetch_config.json diff --git a/app.py b/app.py index c8ba557..c0c08d5 100644 --- a/app.py +++ b/app.py @@ -20,13 +20,17 @@ IMAGES = { "docker_image": "micro-fedora43-dev:latest", "label": "Fedora 43 Dev Box (micro-fedora43-dev)" }, + "alpine": { + "docker_image": "micro-alpine-dev:latest", + "label": "Alpine Linux Dev Box (micro-alpine-dev)" + }, } DEFAULT_IMAGE_KEY = "debian" DATA_ROOT = "/srv/microcontainers" # per-tenant metadata + disk.img MOUNT_ROOT = "/mnt/microcontainers" # where disk.img files are mounted -HOSTNAME = "arthur.lan" # or "mentalnet.xyz" if you prefer +HOSTNAME = "localhost" # or "mentalnet.xyz" if you prefer PORT_RANGE_START = 20000 PORT_RANGE_END = 21000 @@ -49,6 +53,33 @@ def find_free_port(start=PORT_RANGE_START, end=PORT_RANGE_END): raise RuntimeError("No free ports available in range") +def setup_tenant_environment(home_mount: str): + """ + Create default .bashrc, fastfetch config, and other user environment files. + Runs AFTER the home mount is created. + """ + + bashrc = os.path.join(home_mount, ".bashrc") + config_dir = os.path.join(home_mount, ".config", "fastfetch") + os.makedirs(config_dir, exist_ok=True) + + # Copy fastfetch config + source_cfg = os.path.join(os.path.dirname(__file__), "fastfetch_config.jsonc") + dest_cfg = os.path.join(config_dir, "config.jsonc") + + if os.path.exists(source_cfg): + subprocess.run(["cp", source_cfg, dest_cfg], check=True) + + # Write a curated .bashrc + with open(bashrc, "a") as f: + f.write("\n# ----- MicroContainers defaults -----\n") + f.write("if command -v fastfetch >/dev/null 2>&1; then fastfetch; fi\n") + f.write('alias fastfetch="fastfetch --config $HOME/.config/fastfetch/config.jsonc"\n') + + # Give proper ownership + subprocess.run(["chown", "-R", "1000:1000", home_mount], check=True) + + def validate_ssh_key(key: str) -> bool: """Very basic SSH public key validation.""" key = key.strip() @@ -76,46 +107,24 @@ def is_mounted(mount_point: str) -> bool: return False -def ensure_tenant_disk(container_name: str, ssh_key: str, size_gb: int = 3) -> str: - """ - Ensure a per-tenant disk.img exists, is formatted, mounted, and has - .ssh/authorized_keys populated. +def ensure_tenant_home_dir(container_name: str, ssh_key: str) -> str: + home_dir = os.path.join(DATA_ROOT, container_name, "home") + ssh_dir = os.path.join(home_dir, ".ssh") - Returns the host mount point path, which will be bind-mounted to /home/micro. - """ - tenant_root = os.path.join(DATA_ROOT, container_name) - os.makedirs(tenant_root, exist_ok=True) - - disk_img = os.path.join(tenant_root, "disk.img") - mount_point = os.path.join(MOUNT_ROOT, container_name) - os.makedirs(mount_point, exist_ok=True) - - # 1) Create sparse disk.img if it doesn't exist - if not os.path.exists(disk_img): - subprocess.run(["fallocate", "-l", f"{size_gb}G", disk_img], check=True) - subprocess.run(["mkfs.ext4", "-F", disk_img], check=True) - - # 2) Mount it if not already mounted - if not is_mounted(mount_point): - subprocess.run(["mount", "-o", "loop", disk_img, mount_point], check=True) - - # 3) Prepare /home/micro layout inside the filesystem - # (we're mounting this whole thing as /home/micro in the container) - ssh_dir = os.path.join(mount_point, ".ssh") os.makedirs(ssh_dir, exist_ok=True) + # Write authorized_keys auth_keys_path = os.path.join(ssh_dir, "authorized_keys") with open(auth_keys_path, "w") as f: f.write(ssh_key.strip() + "\n") - # Proper perms for SSH os.chmod(ssh_dir, 0o700) os.chmod(auth_keys_path, 0o600) - # Make everything in /home/micro look like micro:micro (uid/gid 1000) - subprocess.run(["chown", "-R", "1000:1000", mount_point], check=True) + # Make it owned by micro:micro (uid 1000) + subprocess.run(["chown", "-R", "1000:1000", home_dir], check=True) - return mount_point + return home_dir def run_docker_container(container_name: str, host_port: int, docker_image: str, home_mount: str): @@ -304,9 +313,12 @@ def index(): base_name = f"mc-{image_key}-{safe_note}" container_name = f"{base_name}-{suffix}" - # Ensure per-tenant disk (3 GB ext4) and authorized_keys + # Ensure per-tenant disk (1 GB ext4) and authorized_keys try: - home_mount = ensure_tenant_disk(container_name, ssh_key, size_gb=3) + home_mount = ensure_tenant_home_dir(container_name, ssh_key) + setup_tenant_environment(home_mount) + + except subprocess.CalledProcessError as e: flash(f"Error preparing tenant disk: {e}") return render_template_string( diff --git a/fastfetch_config.jsonc b/fastfetch_config.jsonc new file mode 100644 index 0000000..cd6abaa --- /dev/null +++ b/fastfetch_config.jsonc @@ -0,0 +1,15 @@ +{ + "display": { + "separator": " == " + }, + "modules": [ + "title", + "os", + "kernel", + "cpu", + "memory", + "disk", + "shell" + ] +} + diff --git a/micro-alpine-dev/Dockerfile b/micro-alpine-dev/Dockerfile new file mode 100644 index 0000000..5a0493a --- /dev/null +++ b/micro-alpine-dev/Dockerfile @@ -0,0 +1,53 @@ +FROM alpine:latest + +# Core packages (Alpine equivalents) +RUN apk update && apk add --no-cache \ + openssh \ + sudo \ + ca-certificates \ + git \ + curl wget \ + vim nano \ + htop \ + build-base \ + fastfetch + +# Create 'micro' user with UID 1000 and primary group 'micro' +RUN addgroup -g 1000 micro && \ + adduser -D -u 1000 -G micro -s /bin/sh micro && \ + echo "micro:ChangeMe123" | chpasswd + +# Create sudo group and add micro to it +RUN addgroup -S sudo && \ + adduser micro sudo && \ + # Enable sudo for %sudo group in /etc/sudoers + sed -i 's/# %sudo/%sudo/' /etc/sudoers + +# Prepare .ssh directory +RUN mkdir -p /home/micro/.ssh && \ + chown -R micro:micro /home/micro && \ + chmod 700 /home/micro/.ssh + +# SSH server config: key-only login, use ~/.ssh/authorized_keys +RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config || true && \ + sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config || true && \ + sed -i 's/#KbdInteractiveAuthentication yes/KbdInteractiveAuthentication no/' /etc/ssh/sshd_config || true && \ + sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config || true && \ + sed -i 's|#AuthorizedKeysFile.*|AuthorizedKeysFile .ssh/authorized_keys|' /etc/ssh/sshd_config || true && \ + echo 'UsePAM no' >> /etc/ssh/sshd_config + +# sshd runtime dirs + host keys +RUN mkdir -p /var/run/sshd && \ + ssh-keygen -A + +# Fastfetch config for micro +RUN mkdir -p /home/micro/.config/fastfetch +COPY fastfetch_config.json /home/micro/.config/fastfetch/config.jsonc +RUN chown -R micro:micro /home/micro/.config && \ + echo 'if command -v fastfetch >/dev/null 2>&1; then fastfetch; fi' >> /home/micro/.profile && \ + chown micro:micro /home/micro/.profile + +EXPOSE 22 + +CMD ["/usr/sbin/sshd", "-D"] + diff --git a/micro-alpine-dev/build-dockerfile.sh b/micro-alpine-dev/build-dockerfile.sh new file mode 100755 index 0000000..f3a702f --- /dev/null +++ b/micro-alpine-dev/build-dockerfile.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +IMAGE_NAME="$(basename "$(pwd)")" + +echo "Building Docker image: ${IMAGE_NAME}:latest" +docker build -t "${IMAGE_NAME}:latest" . + diff --git a/micro-alpine-dev/fastfetch_config.json b/micro-alpine-dev/fastfetch_config.json new file mode 100644 index 0000000..cd6abaa --- /dev/null +++ b/micro-alpine-dev/fastfetch_config.json @@ -0,0 +1,15 @@ +{ + "display": { + "separator": " == " + }, + "modules": [ + "title", + "os", + "kernel", + "cpu", + "memory", + "disk", + "shell" + ] +} +