In a previous post I mentioned the approach I took to secure an RDS server behind a bastion server being hosted on AWS. Using SSH to create a tunnel to access this resources is well documented and so there is no need to labour on that. However I also needed to access these resources from applications that are running on Docker containers running elsewhere. I am sure there is more than one way to achieve this but I will describe the approach I took and has so far proven to be pretty stable.
I dismissed any code based options as they just made the application more complex to develop and test and instead went for an approach that was purely container based. To do this I used two applications
- AutoSSH - to set up the SSH tunnel and maintain it,
- Supervisor - to execute and run/monitor multiple programs.
AutoSSH is obvious but why did I need Supervisor? Well a container only has a single ENTRYPOINT (or CMD) which can be used to run an application or a script but in this case I needed to run several (at least two) applications at the same time and for both applications to stay running. This could be managed by scripts but I decided to give Supervisor a try instead.
Adding Supervisor and AutoSSH to the container
Initially I was running a dotnet application and launching it like so in my Dockerfile
FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "My.Application.dll"]
I updated this section to fetch AutoSSH and Superisor and install them
FROM base AS final # install additional packages RUN mkdir -p /var/log/supervisor /run/sshd RUN apt-get update && apt-get install -y supervisor autossh # configure supervisor RUN mkdir -p /var/log/supervisor COPY ["web/My.Application/supervisord.conf", "/etc/supervisor/conf.d/supervisord.conf"] # install certificates COPY ["web/My.Application/certs/rds-ca-2019-root.crt", "/usr/local/share/ca-certificates/"] RUN chmod 644 /usr/local/share/ca-certificates/rds-ca-2019-root.crt COPY ["web/My.Application/certs/rds-ca-2019-ap-southeast-2.crt", "/usr/local/share/ca-certificates/"] RUN chmod 644 /usr/local/share/ca-certificates/rds-ca-2019-ap-southeast-2.crt RUN update-ca-certificates # copy application WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
Finally in the
supervisord.conf I configured it to launch AutoSSH and the original dotnet application.
[supervisord] nodaemon=true logfile=/share/conf/logs/supervisord/supervisord.log [program:dotnet] command=/usr/bin/dotnet /app/My.Application.dll [program:autossh] command=autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -i %(ENV_SSH_ARGS_KEYPATH)s -N -L %(ENV_SSH_ARGS_MAPPING)s "-o UserKnownHostsFile=/dev/null" "-o StrictHostKeyChecking=no" %(ENV_SSH_ARGS_HOSTNAME)s stdout_logfile=/share/conf/logs/supervisord/autossh.log redirect_stderr=true
/share folder is actually a file mount so that we can preserve the logs for later analysis. The key file is also hosted on a secured file mount and passed in via the environment variable
It is probably overkill to use Supervisor like this but it was a lot simpler (and I feel easier for others to follow) than writing lots of shell scripts to achieve the same.
As always your feedback is appreciated.