This experiment demonstrates using Selenium client containers to test browser interaction with a Flask website. It's all HTTPS (TLS, that is).
The application and test source code is provided by means of a data volume container.
Follow the guidance in ../certs/README.md to create the root CA certificate and server certificate that will be used in this example.
Chrome, other browsers (except Firefox), and curl use the system trusted store as the source of root certificates used to validate the signed certificates presented by HTTPS endpoints. So you need to create Selenium node images that have the test root CAs in the system trusted store.
Create those images by running the build.sh script from these directories:
images/selenium-standalonechrome-tlsimages/selenium-standalonefirefox-tls
Note: Tested with Firefox 50.1.0 on OS X El Capitan (10.11.6).
Firefox doesn't use the system store of trusted certifications, but instead uses a private database of trusted certificates that is part of a specific browser profile. You'll need to create that database file so that the test case can provide it as part of the browser profile used during testing.
Essentially the steps for doing this—from a desktop system with Firefox installed—are:
- Close any instances of Firefox running on the system.
- Launch Firefox's profile manager and create a clean new profile.
- Execute
/Applications/Firefox.app/MacOS/firefox -P, to launch the profile dialog. - Click "create profile".
- Click "continue".
- Set the profile name (I picked "selenium").
- Note the directory path where the profile will be stored
- Click "done"
- Execute
- Select the new profile and click "start firefox".
- Add CA certificate to Firefox.
- Go to Preferences and select Advanced in the nav bar on the left.
- Then select Certificates in the nav bar on the top.
- Click the "View Certificates" button
- In the resulting dialog, click the "Import" button.
- Browse to the CA certificate file (e.g.,
./certs/root_ca_2017/rootCAcert.pem) and import it. - When prompted, check "Trust this CA to identify websites", and click OK to confirm the change.
- Click OK again to close the View Certificates dialog.
- Close Firefox.
- Launch Firefox's profile manager and set the proper default profile.
- Execute
/Applications/Firefox.app/MacOS/firefox -P, to launch the profile dialog. - Select your normal profile.
- Make sure the "Use selected profile without asking at startup" checkbox is checked.
- Click "Start Firefox".
- Execute
- Copy the
cert8.dbfile tohelloworld-tls\src\firefox_trusted_certs- Create the directory
firefox_trusted_certsinhelloworld-tls\src\firefox_trusted_certs, if it doesn't already exist. - From the profile directory of the profile you created and to which you added the root CA certificate (in step 4 above), copy
cert8.dbintohelloworld-tls\src\firefox_trusted_certs
- Create the directory
To get the Selenium Firefox client to accept the server's DV certificate, the trusted root certificate that signed the DV cert must be known to Firefox via the browser profile.
The only way I know to do that is to run Firefox on a desktop OS somewhere, add the trusted CA cert to that profile, and then copy the file cert8.db from the profile and into src\firefox_trusted_certs.
With a populated cert8.db in that location, push the source tree to the data volume container and then run the tests. The Firefox test fixture will create a new Firefox_Profile instance using that cert8.db file and use that profile to instantiate the remote Firefox Selenium client.
Chrome uses the system's trusted root certificate store, so you'll need to have added the trusted root certificate that signed the server's DV certificate in that store.
In this example, that was done when building the docker images used for the Selenium client nodes (specifically via selenium-standalonechrome-tls).
Preconditions:
- you have built up all the images (including the
*-tlsimages) as described in the ../images/README.md file. - you have populated the
helloworld-tls\src\firefox_trusted_certsdirectory as described in the section "Create the Firefox certificate database".
To run the example:
- Edit
/etc/hostsand add an entry mapping the IP address of the Docker host machine to the nametester.experiment.dev. cdto thehelloworld-tlsdirectory.- Establish the source code data volume container by executing
./push-src.sh - Establish the server certificate data volume container by executing
./update-cert-dv.sh - Run the example by executing
./test-up-follow-down.sh.
You should see the successful execution of two tests cases (one using Chrome and one using Firefox). The output will be something like this:
[master]~/Documents/xmp/Selenium-and-TLS/helloworld-tls$
./test-up-follow-down.sh
Creating network "helloworldtls_app-test-net" with driver "bridge"
Creating helloworldtls_crdriver_1
Creating helloworldtls_ffdriver_1
Creating helloworldtls_tester_1
test_admin_home_page (test_selenium_cr.ChromiumSeleniumTestCase) ... ok
test_admin_home_page (test_selenium_ff.FirefoxSeleniumTestCase) ... ok
----------------------------------------------------------------------
Ran 2 tests in 5.617s
OK
Stopping helloworldtls_ffdriver_1 ... done
Stopping helloworldtls_crdriver_1 ... done
Removing helloworldtls_tester_1 ... done
Removing helloworldtls_ffdriver_1 ... done
Removing helloworldtls_crdriver_1 ... done
Removing network helloworldtls_app-test-net
Note: If you want to see what the website looks like, you can execute run-server.sh and then point your browser to https://tester.experiment.dev. You'll need to launch Firefox using the profile you created above (so that the root CA cert can be used to validate the site certificate); for Chrome, you'll need to add the root CA cert to the system trusted store (e.g., see selenium-standalonechrome-tls/Dockerfile for how to update the Ubuntu system trusted store).
push-src.sh: Create or recreate the source data volume container.run-server.sh: Just runs the website in a container with port 443 exposed. You can point a browser at this and see that the site is operating. This does not run any automated tests.test-down.sh: Tears down the containers and networks created bytest-up.sh. You need to run this;test-up.shdoes not automatically clean up after itself.test-up.sh: Uses Docker Compose to launch Chrome and Firefox Selenium client nodes and the website test node. The test node executes tests that use the Selenium clients to interact with a web endpoint stood up by the test harness. Usedocker logsto see the test log and test results from the test container. Runpush-src.shorupdate-src.shbefore launchingtest-up.shto be sure you test the latest source code.test-up-follow-down.sh: Uses Docker Compose to launch Chrome and Firefox Selenium client nodes and the website test node, displays the test execution log to the console, and then tears down the containers and networks instantiated by Docker Compose. The test node executes tests that use the Selenium clients to interact with a web endpoint stood up by the test harness. Runpush-src.shorupdate-src.shbefore launchingtest-up.shto be sure you test the latest source code.update-cert-dv.sh: Create or update the server certificate data volume container.update-src.sh: Update the source data volume container to match what is in thesrcdirectory. Do this if you change the code.
All the image names are prefixed with datihein/. That's what I use for namespacing all my private experimental images, those I use for experiments or testing and never intend to push into a repository. If you want to use a different prefix, go ahead ... but you'll need to do a global replace because there are many scripts and Dockerfiles that specify image names with that prefix.
The way I've used Flask in this example is very much influenced by my recent experience working through the O'Reilly book "Flask Web Development" (circa 2014), by Miguel Grinberg.
--
Copyright 2017 David Hein
Licensed under the MIT License. If the LICENSE file is missing, you can find the MIT license terms here: https://opensource.org/licenses/MIT.