Citrix Secure Developer Spaces™

Build and manage custom workspace images for Citrix SDS

While standard images provide a quick start, using custom workspace images within Citrix Secure Developer Spaces (SDS) ensures your development environment is tailored to your organization’s needs while maintaining strict compliance.

Benefits of custom images

  • Tailored developer experience: Pre-install project-specific runtimes (Node, Python, Go) and CLI utilities (kubectl, terraform). You can pre-configure internal network settings, CA certificates, IDE extensions, and shell aliases so the workspace is ready immediately upon launch.
  • Standardized governance: Ensure every container follows internal standards, including approved, hardened OS distributions and software provenance from trusted repositories.
  • Proactive security: Integrate images into your vulnerability management lifecycle. Trigger automated builds to rotate images when a CVE is detected and reduce the attack surface by removing unnecessary packages.

Architecture

Runtime architecture

In the SDS platform, a workspace runs inside a Kubernetes Pod. There is a one-to-one relationship between a workspace and a Pod to ensure container-level isolation.

The workspace runtime consists of three primary layers:

  • The workspace container: The execution environment containing your compilers, debuggers, and tools.
  • Persistent storage: A persistent volume (PV) mounted in the user home directory (/home/developer). This ensures that code, local configurations, and shell history persist if the Pod restarts.
  • Platform sidecars: Auxiliary containers managed by the SDS platform that provide features like startup scripts and Docker-in-Docker support.

Workspace Image

Layered image architecture

We recommend a multi-layered image architecture to balance centralized governance with developer autonomy:

  • Base Image: A hardened OS provided by Corporate IT. Includes enterprise CA certificates and core security configurations.
  • Organization Image: Owned by the SDS Platform team. Contains core SDS dependencies, common development tools, and global proxy settings.
  • Project Image: Owned by application teams. Includes specific runtimes (e.g., JDK 17, Node 20) and project-specific dependencies.

Understanding persistence

In an SDS workspace, only the /home/developer directory is persistent. When a workspace restarts, any changes to the root filesystem (/etc, /usr, /var) are discarded.

Important:

The persistent volume is mounted at runtime. Any files placed in /home/developer during the docker build phase will be overwritten by the persistent volume once the workspace starts.

Persistence design patterns

  • Binaries: Install binaries in system directories like /usr/local/bin during the Docker build.
  • Local data and configuration: Configure tools to store logs, cache, and plugins in /home/developer.
  • Global configuration: Use /etc/ for global tool settings to serve as a fallback.

Non-standard persistence

Tools that dynamically install binaries to the home directory (e.g., conda, nvs, gcloud CLI) must be treated as Local Data. Use startup scripts to ensure these configurations are restored or initialized after the persistent volume is mounted.

Automated configuration with startup scripts

Platform-managed scripts

Managed via the SDS UI, these scripts are executed every time a workspace starts.

  • Pre-startup scripts: Run during the initial container initialization phase.
  • Post-startup scripts: Run after the workspace services are active.
  • Idempotency: Because these run on every start, ensure scripts check for existing configurations:
if [ ! -f "/home/developer/.my_tool_config" ]; then
    echo "Initializing configuration..."
fi
<!--NeedCopy-->

Image-embedded scripts

Best for strict DevOps practices, these scripts are stored in the Docker image and run only once during the initial workspace creation.

  • Target directory: /usr/bin/strong_network_startup/
  • Permissions: The developer user must have execution permissions.
  • Naming: Scripts run in alphabetical order. Use numeric prefixes (e.g., 01_setup.sh).

Security best practices

Secret handling

Never embed credentials (SSH keys, API tokens) directly in a Docker image. SDS provides secure injection at runtime:

  • Environment variables: Best for API keys and usernames.
  • File mounts: Secrets are mounted in the /secrets/ folder. Preferred for license files and cryptographic keys.

Third-party integrations

Always prefer native platform integrations (e.g., JFrog Artifactory, Git providers) over manual secret injection. SDS handles the authentication layer automatically, so you do not need to manually manage .npmrc, .gitconfig, or .docker/config.json files in your image.

System requirements

Package requirements

Package Purpose
glibc >= 2.28 C standard library (Required for VS Code Server)
libstdc++ >= 3.4.25 C++ standard library
bash Default shell required by VS Code server
git & git-lfs Essential for source control and large file support
openssh-client Required for SSH-based git authentication
tar Required for unpacking the server bundle

Distribution-specific requirements

  • Ubuntu/Debian: Use Ubuntu 20.04+ or Debian 11+ for glibc compatibility. Include ca-certificates.
  • RHEL/CentOS Stream: Supports versions 8 and 9. CentOS 7 is not supported due to glibc limitations.
  • Alpine Linux: Requires musl >= 1.2.3 and the gcompat layer for glibc compatibility.
  • ARM32: Requires libatomic1 for the Node.js runtime.

User account requirements

  • Username: developer
  • Home Directory: /home/developer
  • UID/GID: 1000

Build your first base image

Design philosophy

  • Completeness over minimality: Unlike production images, workspace images should prioritize developer experience by including all necessary compilers, headers, and -dev libraries.
  • Skip multi-stage builds: Keep tools installed during the build process available for the developer.
  • Orchestration overrides: SDS overrides ENTRYPOINT and CMD to launch platform services. Use startup scripts for initialization logic.

Ubuntu 24.04 example

FROM ubuntu:24.04

# 1. Install SDS core requirements

RUN apt-get update && apt-get install -y --no-install-recommends \
    curl bash tar git git-lfs openssh-client sudo \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

# 2. Create developer user (Repurposing default 'ubuntu' user for 24.04)

RUN usermod -l developer -d /home/developer -m ubuntu && \
    groupmod -n developer ubuntu

# 3. Setup startup directory

RUN mkdir -p /usr/bin/strong_network_startup/vscode_extensions && \
    chown -R developer:developer /usr/bin/strong_network_startup && \
    chmod u+rwx /usr/bin/strong_network_startup
<!--NeedCopy-->

Enabling root access

To grant the developer user sudo privileges, add the following to your Dockerfile:

RUN apt-get update && apt-get install -y sudo && rm -rf /var/lib/apt/lists/*

# Grant passwordless sudo

RUN echo "developer ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/developer && \
    chmod 0440 /etc/sudoers.d/developer
<!--NeedCopy-->

Note:

For increased security, you can allowlist specific tools instead of granting full access: developer ALL=(ALL) NOPASSWD: /usr/bin/apt-get, /usr/bin/systemctl