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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .devcontainer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
credentials.netrc
kas.gitconfig
timezone.env
65 changes: 51 additions & 14 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,70 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
{
"name": "EBcL SDK",
"image": "artifactory.elektrobit.com/eb_corbos_linux-snapshots-docker/ebcl-sdk/devcontainer-amd64:main",
"name": "EB corbos Toolkit",
// the container is updated by CI directly "in place" with the same tag,
// so we always need to pull the latest image manually before starting the container
"image": "ghcr.io/elektrobit/eb-corbos-toolkit-devcontainer-amd64:v18",
// set the tag to local if you are using a locally built image
//"image": "artifactory.elektrobit.com/eb_corbos_linux-releases-docker/ebcl-sdk/devcontainer-amd64:local",
"postStartCommand": "${PWD}/.devcontainer/enter_container.sh",
"postCreateCommand": "${PWD}/.devcontainer/setup_binfmt_support.sh",
// "image": "ghcr.io/elektrobit/eb-corbos-toolkit-devcontainer-amd64:local",
"initializeCommand": ".devcontainer/scripts/initialize.sh",
"postStartCommand": {
"Setup kas git config": ".devcontainer/scripts/setup_kas_gitconfig.sh",
"Check credentials": ".devcontainer/scripts/setup_credentials.sh --check",
"Setup target connections": ".devcontainer/scripts/setup_targets.sh",
"Entering container": ".devcontainer/scripts/enter_container.sh"
},
"postCreateCommand": {
"Setup binfmt support": ".devcontainer/scripts/setup_binfmt_support.sh",
"Setup persistent bash history": ".devcontainer/scripts/setup_command_history.sh"
},
"containerEnv": {
"GITCONFIG_FILE": "/workspace/.devcontainer/kas.gitconfig",
// Unfortunately, BitBake requires this in any case - even if the .netrc file is located in the default location.
"NETRC_FILE": "/home/developer/.netrc"
},
"containerUser": "developer",
"mounts": [
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/developer/.ssh,type=bind,consistency=cached",
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.gnupg,target=/home/developer/.gnupg,type=bind,consistency=cached",
"source=/dev,target=/dev,type=bind"
{
"source": "${localEnv:HOME}${localEnv:USERPROFILE}/.ssh",
"target": "/home/developer/.ssh",
"type": "bind"
},
{
"source": "${localWorkspaceFolder}/.devcontainer/credentials.netrc",
"target": "/home/developer/.netrc",
"type": "bind"
},
{
"source": "/dev",
"target": "/dev",
"type": "bind"
},
{
"source": "eb-corbos-toolkit-bash-history-${devcontainerId}",
"target": "/commandhistory",
"type": "volume"
}
],
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
"workspaceFolder": "/workspace",
"privileged": true,
"runArgs": [
"--hostname", "ebcl-sdk"
"--hostname",
"eb-corbos-toolkit",
"--env-file",
"${localWorkspaceFolder}/.devcontainer/timezone.env"
],
"customizations": {
"vscode": {
"extensions": [
"ms-python.python@2026.5.2026032701",
"ms-vscode.cpptools-extension-pack@1.3.1"
]
},
"settings": {
"cmake.configureOnOpen": false,
"taskExplorer.showHiddenWsTasks": false
],
"settings": {
"cmake.configureOnOpen": false,
"taskExplorer.showHiddenWsTasks": false
}
}
}
}
5 changes: 0 additions & 5 deletions .devcontainer/enter_container.sh

This file was deleted.

4 changes: 4 additions & 0 deletions .devcontainer/lib/common.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2026 Elektrobit. All rights reserved.

# Common devcontainer variables
CREDENTIALS_NETRC="$(dirname "$(dirname "$(realpath "${BASH_SOURCE[0]}")")")/credentials.netrc"
88 changes: 88 additions & 0 deletions .devcontainer/lib/lib.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
# Copyright 2026 Elektrobit. All rights reserved.
# Shared functions for devcontainer lifecycle and setup scripts.

[[ -n "${_DEVCONTAINER_LIB_SH:-}" ]] && return 0
_DEVCONTAINER_LIB_SH=1

dc_script_dir() {
cd -- "$(dirname -- "${BASH_SOURCE[1]}")" && pwd
}

dc_devcontainer_root() {
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd
}

dc_init_colors() {
if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then
C_BOLD="$(printf '\033[1m')"
C_RED="$(printf '\033[31m')"
C_YELLOW="$(printf '\033[33m')"
C_GREEN="$(printf '\033[32m')"
C_RESET="$(printf '\033[0m')"
else
C_BOLD="" C_RED="" C_YELLOW="" C_GREEN="" C_RESET=""
fi
}

dc_info() {
[[ -n "${QUIET:-}" ]] || printf '%s\n' "$*"
}

dc_warn() {
printf '%b%s%b\n' "${C_YELLOW:-}" "$*" "${C_RESET:-}" >&2
}

dc_error() {
printf '%b%s%b\n' "${C_RED:-}" "$*" "${C_RESET:-}" >&2
}

dc_success() {
printf '%b%s%s%b\n' "${C_BOLD:-}" "${C_GREEN:-}" "$*" "${C_RESET:-}"
}

dc_sudo() {
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
printf 'sudo'
fi
}

dc_banner() {
local title="${1:-}"
local color="${2:-${C_YELLOW:-}}"
local line="============================================================"

printf '\n%b%s%b\n' "$color" "$line" "${C_RESET:-}"
printf '%b %s%b\n' "$color" "$title" "${C_RESET:-}"
printf '%b%s%b\n\n' "$color" "$line" "${C_RESET:-}"
}

dc_run_plugins() {
local plugin_dir="$1"
shift
local status=0
local had_nullglob=0
local plugin
local plugins=()

shopt -q nullglob && had_nullglob=1
shopt -s nullglob
[[ -d "$plugin_dir" ]] && plugins=("$plugin_dir"/*.sh)
(( had_nullglob )) || shopt -u nullglob

if (( ${#plugins[@]} == 0 )); then
dc_info "No setup files found in ${plugin_dir}. Skipping..."
return 0
fi

for plugin in "${plugins[@]}"; do
if ! bash "$plugin" "$@"; then
status=1
fi
done
return "$status"
}

dc_finish() {
printf 'Finished execution of %s!\n' "$0"
}
174 changes: 174 additions & 0 deletions .devcontainer/scripts/check_userns_restriction.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/usr/bin/env bash
# Copyright 2026 Elektrobit. All rights reserved.
#
# Detect the Ubuntu 23.10+ AppArmor restriction of unprivileged user
# namespaces and guide the user to fix it.
# This host-side check is non-fatal so it can never prevent container startup.
#
# Since Ubuntu 23.10 Canonical restricts the creation of unprivileged user
# namespaces via AppArmor. BitBake relies on user namespaces (e.g. to disable
# network access for a task by writing to /proc/self/uid_map). When the
# restriction is active this fails with:
#
# PermissionError: [Errno 1] Operation not permitted (writing uid_map)
#
# and the affected BitBake task aborts. The fix is a small AppArmor profile
# that grants the "userns" permission to the BitBake executable.
#
# This script is meant to run on the *host* (AppArmor is a host-kernel feature),
# which is why it is invoked from initialize.sh, the only devcontainer
# lifecycle script that runs on the host before the container starts.
#
# It is intentionally non-fatal: it never aborts container startup. When it
# cannot fix the problem automatically it prints clear manual instructions.
#
# Usage: check_userns_restriction.sh [--yes] [--quiet]
# --yes Apply the fix without asking for confirmation (implies sudo use).
# --quiet Suppress the "everything is fine" / "not applicable" messages.
#
# Environment overrides (mainly for testing):
# USERNS_SYSCTL_FILE sysctl proc file to inspect
# (default: /proc/sys/kernel/apparmor_restrict_unprivileged_userns)
# USERNS_APPARMOR_PROFILE AppArmor profile file to create
# (default: /etc/apparmor.d/bitbake)
# USERNS_ASSUME_YES=1 same as --yes

# Note: no "set -e" here on purpose. This script must never abort the caller
# (initialize.sh) and thereby prevent the container from starting.
set -uo pipefail

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
source "${SCRIPT_DIR}/../lib/lib.sh"
dc_init_colors

USERNS_SYSCTL_FILE="${USERNS_SYSCTL_FILE:-/proc/sys/kernel/apparmor_restrict_unprivileged_userns}"
USERNS_APPARMOR_PROFILE="${USERNS_APPARMOR_PROFILE:-/etc/apparmor.d/bitbake}"

ASSUME_YES="${USERNS_ASSUME_YES:-}"
QUIET=""

while [[ $# -gt 0 ]]; do
case "$1" in
--yes|-y) ASSUME_YES=1 ;;
--quiet|-q) QUIET=1 ;;
*) echo "check_userns_restriction: ignoring unknown argument '$1'" >&2 ;;
esac
shift
done

# The AppArmor profile that allows BitBake to create user namespaces.
# The path glob matches the BitBake executable regardless of where the
# workspace is checked out or mounted (host or container).
print_profile() {
cat <<'PROFILE'
abi <abi/4.0>,
include <tunables/global>
profile bitbake /**/bitbake/bin/bitbake flags=(unconfined) {
userns,
}
PROFILE
}

print_manual_instructions() {
cat <<EOF
${C_BOLD}To fix this, run the following commands on your host and then restart:${C_RESET}

sudo tee ${USERNS_APPARMOR_PROFILE} > /dev/null <<'EOP'
$(print_profile)
EOP
sudo apparmor_parser -r ${USERNS_APPARMOR_PROFILE}

Alternatively, disabling the restriction system-wide also works but is less
targeted:

sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

See the "Troubleshooting" section of the user manual for details.
EOF
}

apply_fix() {
if ! command -v apparmor_parser > /dev/null 2>&1; then
dc_error "Cannot apply fix automatically: 'apparmor_parser' not found."
print_manual_instructions
return 1
fi

dc_info "Writing AppArmor profile to ${USERNS_APPARMOR_PROFILE} ..."
if ! print_profile | $(dc_sudo) tee "${USERNS_APPARMOR_PROFILE}" > /dev/null; then
dc_error "Failed to write ${USERNS_APPARMOR_PROFILE}."
print_manual_instructions
return 1
fi

dc_info "Loading AppArmor profile ..."
if ! $(dc_sudo) apparmor_parser -r "${USERNS_APPARMOR_PROFILE}"; then
dc_error "Failed to load the AppArmor profile."
print_manual_instructions
return 1
fi

dc_success "AppArmor profile for BitBake installed and loaded successfully."
return 0
}

main() {
# AppArmor and this restriction only exist on Linux hosts.
if [[ "$(uname -s)" != "Linux" ]]; then
dc_info "Not a Linux host, skipping user namespace restriction check."
return 0
fi

# If the sysctl knob is absent or not active, there is nothing to do.
if [[ ! -r "${USERNS_SYSCTL_FILE}" ]]; then
dc_info "AppArmor user namespace restriction not present on this host."
return 0
fi

local value
value="$(cat "${USERNS_SYSCTL_FILE}" 2>/dev/null || echo 0)"
if [[ "${value}" != "1" ]]; then
dc_info "AppArmor user namespace restriction is not active."
return 0
fi

# Restriction is active. Is the fix already in place?
if [[ -f "${USERNS_APPARMOR_PROFILE}" ]]; then
dc_info "AppArmor user namespace restriction is active, but a BitBake profile" \
"already exists at ${USERNS_APPARMOR_PROFILE}. Nothing to do."
return 0
fi

# Problem detected and not yet fixed: make it impossible to miss.
echo ""
dc_banner "WARNING: Unprivileged user namespaces are restricted" "${C_YELLOW}${C_BOLD}"
echo "This host (Ubuntu 23.10+ or similar) restricts unprivileged user"
echo "namespaces via AppArmor. BitBake needs them and will otherwise fail with:"
echo ""
echo " PermissionError: [Errno 1] Operation not permitted"
echo ""

# Decide whether we may apply the fix automatically.
local do_fix=""
if [[ -n "${ASSUME_YES}" ]]; then
do_fix=1
elif [[ -t 0 ]]; then
local answer=""
read -r -p "Apply the recommended AppArmor fix now (requires sudo)? [y/N] " answer
case "${answer}" in
[yY]|[yY][eE][sS]) do_fix=1 ;;
*) do_fix="" ;;
esac
fi

if [[ -n "${do_fix}" ]]; then
apply_fix || true
else
print_manual_instructions
fi

# Always succeed: never block container startup.
return 0
}

main "$@"
8 changes: 8 additions & 0 deletions .devcontainer/scripts/enter_container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Copyright 2026 Elektrobit. All rights reserved.
# Display the welcome message when entering the devcontainer.

set -euo pipefail

echo "Welcome to the EB corbos Toolkit."
echo "Consult the User's Manual (supplied as part of the delivery) for more information on how to use it."
Loading
Loading