Connect PhpStorm to a Docker Daemon over HTTPS

Goal: Let’s setup a remote Docker daemon and connect to it securely over HTTPS (TLS) with PhpStorm. This will allow us to develop Docker applications remotely inside the PhpStorm IDE. We’ll create certificates, configure the Docker daemon, verify the setup, and configure PhpStorm to even use Docker Compose remotely.

At the end of this article we should have PhpStorm connecting to the Docker daemon in an AWS Linux 2 EC2 container over HTTPS. This is intended for PhpStorm on Windows and AWS users, but it is equally applicable to developers in other IDEs, on OSX, and on other remote OSes. I’ll also explore some common gotchas.

PhpStorm successful connection to remote Docker daemon
PhpStorm successful connection to remote Docker daemon

Requirements:

  • PhpStorm IDE 2017 or higher (optional)
  • A remote Linux server running Docker CE
  • SCP or SFTP
  • OpenSSL

Step 1. Decide on the hostname to connect over HTTPS

Either the Common Name (CN) or one of the Subject Alternative Names (subjectAltName or SAN) of the certificates we’ll generate and the hostname header in the Docker daemon REST API requests must match. You cannot connect securely to just the IP1; you either have to add a hosts entry to your local machine or connect via a DNS-registered domain name that matches the CN or one of the SANs (to be set shortly). The hostname can be anything really, and doesn’t need to be a world-reachable domain name.

The hostname and endpoint I will connect to is https://awsdocker.aws:2376. This is not a real domain, so there is no collision in the real. I’ve added an entry for the public IP of my EC2 instance in my local hosts file (/etc/hosts on Linux) so PhpStorm can connect to this address. Next we’ll create the TLS certificates.

Docker daemon hostname to connect to over HTTPS
Docker daemon hostname to connect to over HTTPS

Step 2. Create the TLS certificates for secure communication

Is AWS Linux 2 we’re logged in as ec2-user. We’re part of the sudoers group so we can use sudo, but let’s minimize superuser activities for safety.

In AWS Linux EC2 you can drop into root with sudo -s without the root password.

All the following certificates will be created in the ec2-user home folder.

Certificate Authority (CA) certificate

First let’s create a Certificate Authority (CA) with a public certificate and private key on the EC2 machine. This represents a trusted third-party that signs the server and client certificates to prove ownership. As long as the same CA’s public certificate is on the remote machine and the local PhpStorm client then trust is maintained.

Generate the CA public certificate and private key
Generate the CA public certificate and private key

Server certificate

Next, let’s generate a server certificate and a signing request. The server certificate must be signed by a trusted CA, so we will use the CA we just created above.

Now let’s configure the SAN entries to allow multiple hostnames, set the key usage, and sign the server certificate.

Generate the Docker daemon server certs
Generate the Docker daemon server certs
You can verify the server certificate SANs and other information with
openssl x509 -in server-cert.pem -noout -text.

Client certificate for PhpStorm

Let’s now generate the private client key and the signing request for the public certificate. We’ll use the same CA keys we used to sign the public server certificate.

Generated the PhpStorm client certs
Generated the PhpStorm client certs
Gotcha: If you specify only “clientAuth” (which intuitively makes sense) you will get the error in PhpStorm “x509: certificate specifies an incompatible key usage” while communicating with the Docker daemon. This is because of the way the underlying Go library handles certificates.
You can verify the client certificate with
openssl x509 -in client-cert.pem -noout -text.

Let’s clean up and set some file permissions.


Step 3. Move the server certificates

Let’s move the Docker daemon certificates to the /etc/docker/certs folder and change the ownership to root.


Review

All the above commands can be copy-and-pasted into a remote terminal for unattended execution. Just be sure to change the hostname and the password.


Step 4. Copy the client certificates locally

We could have generated the client certificates on the development machine if we only copied the CA certificates, but it is more convenient to generate those certificates in one place. So next, secure copy the ca.pem, client-cert.pem, and client-key.pem to the development machine where PhpStorm is to be used. The permissions should be retained. Importantly, rename the client certificates to cert.pem and key.pem respectively to work with PhpStorm.

Copy and rename the certs for PhpStorm
Copy and rename the certs for PhpStorm
Gotcha: Be sure to rename all three files to ca.pem, cert.pem, and key.pem to work with PhpStorm. If you have different names, no matter how descriptive, PhpStorm will complain it cannot find the certificate files with the message “Cannot connect: java.lang.IllegalArgumentException: Can’t locate certificate files under …”.

Step 5. Setup the Docker daemon

We’ll next create a special daemon.json file to configure the Docker daemon to bind to TCP port 2376 and to use the TLS certificates we just created.

Docker daemon.json setting
Docker daemon.json setting

Step 6. Open port 2376

On an ordinary remote Linux machine (non-AWS) we could open incoming TCP port 2376 by updating firewall-cmd or ufw like so.

Amazon AWS has Security Groups that are attached to EC2 instances which are pretty fun and easy to set up. I’ve created two groups – a Web Server group (ports 80 and 443) and a Docker daemon group (port 2376) – and attached them to my EC2 instance. This is cleaner than having one large group of all my firewall rules.

First, let’s create a new Security Group called “Docker daemon”. I restricted Docker access to my development IP and set the TCP port to 2376.

Create a new security group and set the IP and port 2376
Create a new security group and set the IP and port 2376

Next, I assigned the group to my EC2 instance that has the Docker daemon running.

Change the Security Groups of the Docker EC2 instance
Change the Security Groups of the Docker EC2 instance
Assign the Docker Daemon Security Group to the EC2
Assign the Docker Daemon Security Group to the EC2

Finally, I applied the settings and the firewall rules came into effect. Next let’s verify we can connect to the Docker daemon remotely.


Step 7. Verify the secure connection

First we can verify that the Docker daemon has accepted our server certificate. There should be no error messages.

Successfully set up server and client certificates
Successfully set up server and client certificates

My client certificate, key and the CA certificate are copied to my local environment, and in my hosts file I have “awsdocker.aws” pointing to the public IP address of the EC2 instance with the Docker daemon. Let’s connect remotely via curl and verify we get a JSON response to a request to list the Docker images.

Successful connection to remote Docker daemon with curl
Successful connection to remote Docker daemon with curl

Remember, we cannot connect to the Docker daemon by IP directly, or with another hostname that is not in the SAN as the screenshot below shows.

Unable to connect with direct IP
Unable to connect with direct IP

Step 8. Configure PhpStorm

This last step is the easiest. In PhpStorm let’s navigate to File > Settings > Build, Execution, Deployment > Docker. Add a new Docker configuration. Select “TCP socket”. The default Engine API URL is tcp://localhost:2375 because PhpStorm assumes in Windows we will install docker locally if we’re not using Docker Machine.

Let’s remember to change “tcp” to “https”, and change the port from 2375 (HTTP) to 2376 (HTTPS) as in the screenshot below. Set the “Certificates folder” path to the location of ca.pem, key.pem, and cert.pem (remember to use those names exactly). That’s it!

PhpStorm successful connection to remote Docker daemon
PhpStorm successful connection to remote Docker daemon

Now we can launch and administer Docker containers from PhpStorm.

Launch and inspect Docker containers from PhpStorm
Launch and inspect Docker containers from PhpStorm

Step 9. Connect PhpStorm to Remote Docker Compose

This is where PhpStorm shows its limitations on Windows. If we want to start a docker-compose process remotely we can open a terminal to the remote server, cd to the docker-compose.yml file folder, and execute docker-compose up -d. If we want to do that with a Docker Compose Deployment in PhpStorm, we just can’t. It’s a real shame. PhpStorm, at least until 2018, enforces that the Docker and Docker Compose binaries are 1) on the host OS as Win32 binaries, and 2) does not perform path mapping. See the screenshot below.

PhpStorm unable to launch a remote docker-compose deployment
PhpStorm unable to launch a remote docker-compose deployment

In case you are wondering, you’re precluded from installing Docker Toolbox or Docker Desktop for Windows inside a virtual machine. Virtual machines already don’t play nicely with additional virtualization (i.e. VirtualBox inside VMWare). Besides, Docker is best run on Linux. With a little fun with macros it is possible to get remote docker-compose to run.

First, let’s inhibit the Docker Machine executable and the Docker Compose executable until JetBrains resolves this. I’ve used a noop.exe do-nothing executable which is just run32dll.exe renamed to silence error messages.

PhpStorm Docker and Compose noop Windows binaries
PhpStorm Docker and Compose noop Windows binaries

Next, let’s add some Remote SSH External Tools entries in Settings. In the screenshot below I’ve created a “docker-compose up -d” tool. The magic comes from the FileRelativePath macro which is replaced with either the current file in the editor, or the file selected from the Project panel.

Set the deployment server to the server where the Docker daemon resides (or have it ask you each time). Make sure the local development files are in sync with the remote files. The working directory is important, and it must be absolute because PhpStorm first executes cd with that path. PhpStorm will convert forward slashes (/) to backslashes (\), but don’t be alarmed.

PhpStorm Remote SSH External Tools with Docker Compose
PhpStorm Remote SSH External Tools with Docker Compose

Let’s be fancy and add some menu shortcuts with these commands. User preferences vary, but we can edit the menus in Appearance & Behavior like so. I’ve also added 16px-by-16px icons I found in one of the jars in the plugins folder.

Add external SSH tools to the menus
Add external SSH tools to the menus
Docker compose actions in the menu
Docker compose actions in the menu
Docker compose external SSH tool successfully runs
Docker compose external SSH tool successfully runs

This works. Though, it would be great if JetBrains could take my example above and make the Docker Compose Run/Debug Configuration behave this way natively. At least this is a viable workaround. I’m gladly open to improvements.

Sample PhpStorm Docker logs
Sample PhpStorm Docker logs

References

It took a while to navigate the error messages and the unwritten requirements (i.e. the cert file names). The following sites helped me assemble this guide.

Notes:

  1. You cannot connect over HTTPS with just the server IP unless you spoof the hostname header of the request (bypass the DNS), or add the IP to the SAN – real certificate authorities never do this.