Pass AWS credentials to a docker container
Docker is great for running your application in various locations in a consistent way: recall the problematic yet common developer adage “It works on my machine!”. What if I’m trying to test something locally that needs to communicate with AWS and I need to authenticate/authorize my way in. This article is my research journal entry on that topic.
Task
I need to run a Java application as a Docker container with code that hits AWS.
Prerequisites
- Application of some sort (I used a Java application)
- Mac (much of this might work on other machines, but my examples are all from a Mac)
- Docker (I use Docker Desktop)
- Shell/Terminal/iTerm
- Git (if you are using that to download a repo)
- Maven (if you are using the example repo)
Download a Functional Java Application (optional)
If you are following along with the Java side of things, you can use this repo, any functional Java repo, or a repo in another language as well as long as it is attempting to communicate with AWS.
Make sure to navigate to the folder you want to clone this repository into.
In your terminal, run the following commands:
To verify that the application works outside of Docker, let’s run it locally.First, build the application.
Next, run it. You can hithttp://localhost:8080/_/health
to verify that the application is up and running. Also, hit the url, with {bucket} and {key} replaced with actual values from your AWS account.
If you get errors along these lines, then you need to set up your .aws
credentials file. If you don’t have your key and secret, then you may need to use a tool like aws-runas to obtain your keys given username/login. - Unable to load credentials from any of the providers in the chain - Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId). - Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId). - Either the environment variable AWS_WEB_IDENTITY_TOKEN_FILE or the javaproperty aws.webIdentityTokenFile must be set. - Profile file contained no credentials for profile ‘default’: - Cannot fetch credentials from container — neither AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variables are set. - Failed to load credentials from IMDS.]
Dockerfile
Now that I’ve proven out my application, I need to define my container. To do that, I need to create a file named Dockerfile. The following is an example:
FROM public.ecr.aws/amazoncorretto/amazoncorretto:11
ENV APP_NAME "myapp"
RUN yum install shadow-utils.x86_64 -y && \
yum install -y which
WORKDIR /opt/java/${APP_NAME}
COPY *.jar app.jar
COPY startup.sh .
RUN chmod +x ./startup.sh
RUN groupadd -r -g 2001 ${APP_NAME}
RUN useradd -r -u 2001 -g ${APP_NAME} ${APP_NAME}
RUN chown ${APP_NAME}.${APP_NAME} /var/log/
RUN chown -R ${APP_NAME}.${APP_NAME} /opt/java/${APP_NAME}
USER ${APP_NAME}
CMD ["./startup.sh"]
Choose Base Image
FROM
defines the base image to pull from. Think of this as a snapshot of a system with the basic software, settings, etc that you need to run your application.
As I am using AWS in my application, I started with images found at Amazon’s Public ECR Gallery. For Java, I used Amazon Corretto, but you could use whatever makes sense for your application.
Install Shadow Utils
Adding RUN yum install shadow-utils.x86_64 -y && yum install -y which
, allows me to create and edit users and groups in Linux later on.
Copy Over Your Code
To copy over my code I first build it, as shown above, and then add the following to copy over my jar file.
COPY *.jar app.jar
- if you have more than one jar file or are having issues with finding you jar file in the container afterward, you may have to be more specific.
Choose a Working Directory
Next, I want to define the main folder that all of my files will be going into. To do this, I can use WORKDIR
.
Startup Script
While I can add my command to start the application directly in the Dockerfile
, I have decided to create a separate file in order to abstract that out so that changes specific to start up will get tracked there and not the Dockerfile
itself.
To do this, I add to my VCS (version control system) a file called startup.sh
. To add the use of this to my Dockerfile
, I add the following lines: - COPY startup.sh
- brings the file into my container - RUN chmod +x /opt/java/myapp/startup.sh
- makes the file executable - CMD ["/.startup.sh"]
- starts the script when running the container. You could also use ENTRYPOINT instead of or in conjunction with CMD, but that is a consideration for another article.
User/Group Considerations
Docker containers use the root user by default. It is generally not a good idea to use the root user when running the application as I want to follow the “least privilege” principle to be more secure. I need a new user specifically for running this application.
I’ll first create a group.
-RUN
- tells the Docker builder to run a command in the container - groupadd
- creates a new group in the Linux container - -r
- makes the group a system group so it doesn’t show up like a regular groups in certain commands - -g
- allows me to define the group id (GID) - myapp
- which is defined via ${APP_NAME} and the env variable above is the name of the group Now I’ll create the user.
-useradd
- creates a new user in the Linux container - -r
- makes the user a system user - -u
- creates a new user with a UID of 2001 - -g
- creates a name myappfor the user and adds them to the myapp group USER myapp
- changes the current user. This should be done at the end of the Dockerfile
, right before the CMD line.
Set the Correct Owner
Make sure your new user has correct ownership or permissions.
chown myapp.myapp /var/log/
- gives the user access to the contents of /var/log/
so that the application can write their logs there.
chown -R myapp.myapp /opt/java/myapp
- gives the user access to run the application.
Build the Docker Container
I have a Dockerfile, but what do I do next?
Build Command
I need to build the new image I defined with my Dockerfile and store it in my Docker cache. I should start in same folder as my Dockerfile, or else specify where to find it.
View Images
To view my image, I can use this command:
Run the Docker Container
Now that I have built the image, to run it I can use the following command:
The argument -p 8080:8080
stands for publish and allows us to open up a port from my local machine and maps it to a port in the container.
Note: After starting up my application, I can kill it by typing Command+c. This is because I used the -t
option and that attaches a psuedo-TTY.
Passing AWS Credentials to Docker Container
Notice that I am only now adding in .aws
directory contents. Why didn’t I add this directly into the image? The answer is security. If my image got out, someone could open it up and find my credentials.
The best way to run something that needs to have AWS credentials is to use IAM Roles. Still, if you are running this locally, that is NOT an option. What should I do?
The argument-v ~/.aws:/home/myapp/.aws
mounts the contents of my personal .aws
folder including config and credentials files to my container user’s home folder so that when it runs the application, it will find and use these. Some containers run under root user. For those you would add:
My application is now running as a Docker container. I can hithttp://localhost:8080/_/health
to verify it is up and running. Alternatively, you could hit whatever your health endpoint is in a browser to verify your application is up and running. I also ran the following to verify that I was able to connect via my .aws
credentials to AWS:
Conclusion
In this article, I have documented my journey on how to deploy a Java application to Docker. I did this by creating a Dockerfile, building it, running it, and testing it out. I was also able to connect to AWS services in my application by mounting my .aws
folder when I ran the container locally.
- Installing Java
- Installing Docker
- Getting Started with Java and Docker
- aws-runas Tool
- ENTRYPOINT vs CMD
- Push Docker Image to AWS
- Using the Official AWS CLI Version 2 Docker Image
- Different Way of Passing Secrets and Credentials into Docker Containers