Step-by-Step Dockerization of a Node.js App Connecting to AWS CloudHSM with PKCS#11 SDK
Recently, I worked on a Node.js application that required integration with AWS CloudHSM for secure cryptographic operations. After successfully implementing the application, the next challenge was to dockerize it so it could run efficiently in an air-gapped environment. This presented its own set of hurdles, as information on how to combine Docker, Node.js, and AWS CloudHSM using the PKCS#11 SDK was scarce. To save others the time and effort I invested, I decided to document the process step-by-step in this blog. If you're facing a similar use case, this guide will walk you through how to efficiently dockerize a Node.js application that connects to AWS CloudHSM using the PKCS#11 SDK. The Challenges of an Air-Gapped Environment In an air-gapped environment, external internet access is often restricted, which means we can't simply pull down images or install packages directly from the web. In our case, we also needed to integrate the AWS CloudHSM PKCS#11 client, which requires specific dependencies. To overcome this, we started by identifying all the necessary dependencies that needed to be baked into the Docker image. Step 1: Setting Up the Base Docker Image We began with an Ubuntu base image (version 20.04) and started by installing a few critical packages, including wget, python3, python3-pip, and curl. These were needed for installing the PKCS#11 SDK and other dependencies. # Use the Ubuntu image FROM ubuntu:20.04 AS builder # Install necessary packages and clean up RUN apt-get update && apt-get install -y \ wget \ python3 \ python3-pip \ curl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* Step 2: Installing Node.js and NPM Next, we needed Node.js and npm to run our application. Since the environment was air-gapped, we had to manually fetch and install the packages. We added the Node.js 16.x setup script and installed it along with npm. # Install Node.js and npm RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \ && apt-get install -y nodejs \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* Step 3: Installing AWS CloudHSM PKCS#11 Client At this point, the main challenge was integrating the AWS CloudHSM PKCS#11 client, which is essential for securely connecting to CloudHSM. We manually downloaded the .deb package for the CloudHSM PKCS#11 client and installed it. # Install CloudHSM PKCS #11 client RUN wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/Focal/cloudhsm-pkcs11_latest_u20.04_amd64.deb \ && apt install -y ./cloudhsm-pkcs11_latest_u20.04_amd64.deb \ && rm -f cloudhsm-pkcs11_latest_u20.04_amd64.deb Step 4: Setting Up the Application Code Once we had the dependencies installed, we created a working directory for our Node.js application. We copied over the package.json files to install the required npm dependencies and then copied the rest of the application code into the container. Finally, we built the application. # Create a work directory WORKDIR /app # Copy package.json files and install npm dependencies COPY package*.json ./ RUN npm cache clean --force \ && npm install \ && npm cache clean --force # Copy the rest of the application code COPY . . RUN npm run build Step 5: Preparing the Final Image After building the application, we transitioned to the final image stage. At this point, we removed unnecessary files and dependencies that weren't needed for the final container. The CloudHSM PKCS#11 client files were copied from the builder stage, and we also ensured that the correct file permissions were set for the libcloudhsm_pkcs11.so file. # Prepare application code for the final stage FROM ubuntu:20.04 # Copy the CloudHSM PKCS #11 client files from the builder stage COPY --from=builder /opt/cloudhsm /opt/cloudhsm # Create a work directory WORKDIR /app COPY --from=builder /app/dist /app/dist COPY --from=builder /app/node_modules /app/node_modules #COPY --from=builder /app/deployment /app/deployment # Permissions to .so file # Optional step RUN chmod -R 777 /opt/cloudhsm/lib RUN chmod -R 777 /opt/cloudhsm/lib/libcloudhsm_pkcs11.so RUN apt-get update && apt-get install -y \ curl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* # Install Node.js and npm RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \ && apt-get install -y nodejs \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* Step 6: Running the Application Finally, you’ll want to run the application. This can be done using a CMD or ENTRYPOINT directive to start your Node.js app within the container. This step depends on how you want to manage your process (whether through a script, direct command, etc.). The Complete Dockerfile Here’s the complete Dockerfile that puts everything together: # Use the Ubuntu image FROM ubuntu:20.04 AS builder # Install necessary packages and
Recently, I worked on a Node.js application that required integration with AWS CloudHSM for secure cryptographic operations. After successfully implementing the application, the next challenge was to dockerize it so it could run efficiently in an air-gapped environment. This presented its own set of hurdles, as information on how to combine Docker, Node.js, and AWS CloudHSM using the PKCS#11 SDK was scarce.
To save others the time and effort I invested, I decided to document the process step-by-step in this blog. If you're facing a similar use case, this guide will walk you through how to efficiently dockerize a Node.js application that connects to AWS CloudHSM using the PKCS#11 SDK.
The Challenges of an Air-Gapped Environment
In an air-gapped environment, external internet access is often restricted, which means we can't simply pull down images or install packages directly from the web. In our case, we also needed to integrate the AWS CloudHSM PKCS#11 client, which requires specific dependencies. To overcome this, we started by identifying all the necessary dependencies that needed to be baked into the Docker image.
Step 1: Setting Up the Base Docker Image
We began with an Ubuntu base image (version 20.04) and started by installing a few critical packages, including wget, python3, python3-pip, and curl. These were needed for installing the PKCS#11 SDK and other dependencies.
# Use the Ubuntu image
FROM ubuntu:20.04 AS builder
# Install necessary packages and clean up
RUN apt-get update && apt-get install -y \
wget \
python3 \
python3-pip \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
Step 2: Installing Node.js and NPM
Next, we needed Node.js and npm to run our application. Since the environment was air-gapped, we had to manually fetch and install the packages. We added the Node.js 16.x setup script and installed it along with npm.
# Install Node.js and npm
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
Step 3: Installing AWS CloudHSM PKCS#11 Client
At this point, the main challenge was integrating the AWS CloudHSM PKCS#11 client, which is essential for securely connecting to CloudHSM. We manually downloaded the .deb package for the CloudHSM PKCS#11 client and installed it.
# Install CloudHSM PKCS #11 client
RUN wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/Focal/cloudhsm-pkcs11_latest_u20.04_amd64.deb \
&& apt install -y ./cloudhsm-pkcs11_latest_u20.04_amd64.deb \
&& rm -f cloudhsm-pkcs11_latest_u20.04_amd64.deb
Step 4: Setting Up the Application Code
Once we had the dependencies installed, we created a working directory for our Node.js application. We copied over the package.json files to install the required npm dependencies and then copied the rest of the application code into the container. Finally, we built the application.
# Create a work directory
WORKDIR /app
# Copy package.json files and install npm dependencies
COPY package*.json ./
RUN npm cache clean --force \
&& npm install \
&& npm cache clean --force
# Copy the rest of the application code
COPY . .
RUN npm run build
Step 5: Preparing the Final Image
After building the application, we transitioned to the final image stage. At this point, we removed unnecessary files and dependencies that weren't needed for the final container. The CloudHSM PKCS#11 client files were copied from the builder stage, and we also ensured that the correct file permissions were set for the libcloudhsm_pkcs11.so file.
# Prepare application code for the final stage
FROM ubuntu:20.04
# Copy the CloudHSM PKCS #11 client files from the builder stage
COPY --from=builder /opt/cloudhsm /opt/cloudhsm
# Create a work directory
WORKDIR /app
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/node_modules /app/node_modules
#COPY --from=builder /app/deployment /app/deployment
# Permissions to .so file
# Optional step
RUN chmod -R 777 /opt/cloudhsm/lib
RUN chmod -R 777 /opt/cloudhsm/lib/libcloudhsm_pkcs11.so
RUN apt-get update && apt-get install -y \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js and npm
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
Step 6: Running the Application
Finally, you’ll want to run the application. This can be done using a CMD or ENTRYPOINT directive to start your Node.js app within the container. This step depends on how you want to manage your process (whether through a script, direct command, etc.).
The Complete Dockerfile
Here’s the complete Dockerfile that puts everything together:
# Use the Ubuntu image
FROM ubuntu:20.04 AS builder
# Install necessary packages and clean up
RUN apt-get update && apt-get install -y \
wget \
python3 \
python3-pip \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js and npm
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install CloudHSM PKCS #11 client
RUN wget https://s3.amazonaws.com/cloudhsmv2-software/CloudHsmClient/Focal/cloudhsm-pkcs11_latest_u20.04_amd64.deb \
&& apt install -y ./cloudhsm-pkcs11_latest_u20.04_amd64.deb \
&& rm -f cloudhsm-pkcs11_latest_u20.04_amd64.deb
# Create a work directory
WORKDIR /app
# Copy package.json files and install npm dependencies
COPY package*.json ./
RUN npm cache clean --force \
&& npm install \
&& npm cache clean --force
# Copy the rest of the application code
COPY . .
RUN npm run build
# Prepare application code for the final stage
FROM ubuntu:20.04
# Copy the CloudHSM PKCS #11 client files from the builder stage
COPY --from=builder /opt/cloudhsm /opt/cloudhsm
# Create a work directory
WORKDIR /app
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/node_modules /app/node_modules
#COPY --from=builder /app/deployment /app/deployment
# Permissions to .so file
RUN chmod -R 777 /opt/cloudhsm/lib
RUN chmod -R 777 /opt/cloudhsm/lib/libcloudhsm_pkcs11.so
RUN apt-get update && apt-get install -y \
curl \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js and npm
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
Conclusion
By following these steps, you can successfully dockerize a Node.js application that connects to AWS CloudHSM using the PKCS#11 SDK, even in an air-gapped environment. The key was to manually manage dependencies and take advantage of Docker’s multi-stage builds to ensure a clean and efficient final image.
I hope this guide saves you some time and frustration, just as it did for me! If you have any questions or run into issues, feel free to leave a comment or reach out. Happy coding!
What's Your Reaction?