{"id":486,"date":"2016-05-11T05:16:02","date_gmt":"2016-05-11T05:16:02","guid":{"rendered":"http:\/\/blog.mozilla.org\/webqa\/?p=486"},"modified":"2016-05-11T05:16:02","modified_gmt":"2016-05-11T05:16:02","slug":"docker-owasp-zap-part-one","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/fxtesteng\/2016\/05\/11\/docker-owasp-zap-part-one\/","title":{"rendered":"Dockerized, OWASP-ZAP security scanning, in Jenkins, part one"},"content":{"rendered":"<p>For a very long time, I\u2019ve been interested in automated security testing. <a href=\"https:\/\/twitter.com\/alan_parkinson\">Alan Parkinson\u2019s<\/a> \u201c<a href=\"https:\/\/www.youtube.com\/watch?v=aVFZFi_6B9g\">Automated Security Testing<\/a>\u201d presentation at <a href=\"http:\/\/lanyrd.com\/2012\/seleniumconf\/\">Selenium Conference 2012<\/a> really highlighted the possibilities, for me. Since then, I\u2019ve wanted to get a simple, yet powerful and effective, automated security-scanning and reporting tool integrated into our development, testing, and release process (which I\u2019ll also refer to as \u201cpipeline.\u201d)<\/p>\n<p>Separately, I\u2019ve also been very interested in exploring and learning <a href=\"http:\/\/www.docker.com\/what-docker\">Docker<\/a>. Last quarter, I was happy to learn that there is a Dockerized OWASP ZAP container, but I didn\u2019t then have the time set aside to learn both Docker and ZAP.  By using Docker to containerize\/Dockerize our OWASP-ZAP instance, we could get it running in our <a href=\"https:\/\/jenkins.io\">Jenkins<\/a> continuous-integration environment, and essentially take the Docker image and run it in other (developers&#8217;, operations&#8217;, etc.) instances.<\/p>\n<p>This quarter, the two are coming together, now that I\u2019ve set aside some focused time and my <a href=\"https:\/\/wiki.mozilla.org\/QA\/Execution\/Web_Testing\/Goals\/2016\/Q2#Stephen\">personal deliverable<\/a> to \u201cGet a Dockererized OWASP ZAP (<a href=\"https:\/\/github.com\/Grunny\/zap-cli\">CLI<\/a>) instance up and running against a staged instance of one of our key sites: either <a href=\"https:\/\/addons.mozilla.org\">AMO<\/a>, <a href=\"https:\/\/www.mozilla.org\">Mozilla.org<\/a>, or <a href=\"https:\/\/developer.mozilla.org\">MDN<\/a>, in <a href=\"https:\/\/webqa-ci.mozilla.com\/\">Web QA&#8217;s Jenkins instance<\/a>, on either\/both a cronjob or on-demand.\u201d<\/p>\n<p>When I was first pointed to <a href=\"https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker\">https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker<\/a> I saw a dizzying array of options: did I want the CLI? ZAPR? Headless? Headless, with Xvfb? GUI VNC?<\/p>\n<p>Because our stack is primarily Python-based, and I\u2019ll want to stand this up in a Jenkins instance, along with, hopefully, our WebDriver tests (which use <a href=\"https:\/\/github.com\/davehunt\/pytest-selenium\">pytest-selenium<\/a>), I chose the ZAP CLI, which is a Python wrapper. It\u2019ll also be lighter weight than the GUI VNC, but should integrate well with our setup.<\/p>\n<p>Let\u2019s start!<\/p>\n<p>So, I downloaded and installed Docker for Mac OS X, from <a href=\"http:\/\/www.docker.com\/\">http:\/\/www.docker.com\/<\/a><\/p>\n<p>Next, I launched the <a href=\"https:\/\/docs.docker.com\/engine\/installation\/mac\/\">Docker Quickstart Terminal<\/a><\/p>\n<p>Then I did:<\/p>\n<p><code>$ docker pull owasp\/zap2docker-stable<\/code><\/p>\n<p>This command pulls the image \u201czap2docker-stable\u201d from Docker\u2019s Hub (specifically, <a href=\"https:\/\/hub.docker.com\/r\/owasp\/zap2docker-stable\/\">https:\/\/hub.docker.com\/r\/owasp\/zap2docker-stable\/<\/a>)<\/p>\n<p>The 1st thing I tried was just a sample command from the <a href=\"https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker#zap-cli\">first set of docs<\/a>, to see if it worked:<\/p>\n<p><code>$ docker run -i owasp\/zap2docker-stable zap-cli quick-scan --self-contained --start-options '-config api.disablekey=true' https:\/\/www.allizom.org<\/code><\/p>\n<p>[INFO] Starting ZAP daemon<br \/>\n[INFO] Running a quick scan for https:\/\/www.allizom.org<br \/>\n[INFO] Issues found: 0<br \/>\n[INFO] Shutting down ZAP daemon<\/p>\n<p>Voila! No issues found &#8211; it started, scanned, and shut down, all very quickly. Good.<\/p>\n<p>Looking to go further with the scan, I glanced at <a href=\"https:\/\/github.com\/Grunny\/zap-cli\">https:\/\/github.com\/Grunny\/zap-cli<\/a> just to get up and running, then tried substituting in ZAP\u2019s <code>active-scan<\/code> command for <code>quick-scan<\/code>, which didn\u2019t work (try it yourself, if you\u2019d like) and after whittling away the given options one by one: <code>--self-contained<\/code>, <code>--start-options<\/code>, and <code>-config<\/code>, I ended up with this, which I tried to run:<\/p>\n<p><code>$ docker run -i owasp\/zap2docker-stable zap-cli active-scan https:\/\/www.allizom.org<\/code><\/p>\n<p>I got this:<\/p>\n<p><code>IOError: [Errno socket error] [Errno 111] Connection refused<\/code><\/p>\n<p>It was then that I was helpfully pointed back to the documentation (<a href=\"https:\/\/github.com\/Grunny\/zap-cli#getting-started-running-a-scan\">https:\/\/github.com\/Grunny\/zap-cli#getting-started-running-a-scan<\/a>), which does point out that zap-cli *only* starts ZAP automatically when using the \u2018<code>quick-scan<\/code>\u2019 option. So, I went back to <a href=\"https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker\">https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker<\/a> and re-read the <a href=\"https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker#zap-headless\">headless<\/a> section, where it shows you how to start ZAP first, in its own container, which, I did, like so:<\/p>\n<p><code>$ docker run -p 8090:8090 -i owasp\/zap2docker-stable zap.sh -daemon -port 8090 -host 0.0.0.0<\/code><\/p>\n<p>That gave me the following output, which told me that ZAP was indeed running:<\/p>\n<p><code>4370 [ZAP-daemon] INFO org.zaproxy.zap.DaemonBootstrap - ZAP is now listening on 0.0.0.0:8090<\/code><\/p>\n<p>So, ZAP is running, but it\u2019s running in a terminal\/console window, and if we CTRL+C, we\u2019ll lose the ZAP process. As a quick exercise, issue the docker run command above, then open a new Docker terminal window (Quickstart, if you\u2019re on OS X), and do a docker ps. You should see something like so:<\/p>\n<p><code>$ docker ps<br \/>\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br \/>\nf6e9a636f8c2 owasp\/zap2docker-stable \"zap.sh -daemon -port\" About a minute ago Up About a minute 0.0.0.0:8090-&gt;8090\/tcp determined_pike<\/code><\/p>\n<p>It\u2019s rather obvious now, but in my initial excitement at having ZAP still running in the above container, I failed to understand how to correctly address\/reach the already-running ZAP, and so when I ran this:<\/p>\n<p><code>$ docker run owasp\/zap2docker-stable zap-cli open-url https:\/\/www.allizom.org<\/code><\/p>\n<p>It naturally gave me the same error I got earlier:<\/p>\n<p><code>IOError: [Errno socket error] [Errno 111] Connection refused<\/code><\/p>\n<p>So, we know ZAP is already running, but the zap-cli isn\u2019t accessing it. What can we do? <a href=\"https:\/\/docs.docker.com\/engine\/reference\/run\/\">Docker\u2019s docs on run<\/a> specify that the command is for running an image, which we\u2019ve already done. That\u2019s the \u2018owasp\/zap2docker-stable\u2019 in all of our above commands.<\/p>\n<p>So, what we want, instead of running another new image or even a new container, is to run our zap-cli command *against* (i.e. to use) the already-running ZAP container, which is \/from\/ the owasp\/zap2docker image.<\/p>\n<p>For that, Docker\u2019s doc says to use <a href=\"https:\/\/docs.docker.com\/engine\/reference\/commandline\/exec\/\"><code><strong>exec<\/strong><\/code><\/a>, which \u201cruns a new command in a running container.\u201d Perfecto! I found this <a href=\"http:\/\/stackoverflow.com\/questions\/30960686\/difference-between-docker-attach-and-docker-exec\">StackOverflow article on \u201cexec vs. attach<\/a>\u201d helpful.<\/p>\n<p>You can find and address Docker containers by either their name or their unique ID, both of which Docker will create for you. See <a href=\"https:\/\/docs.docker.com\/engine\/reference\/run\/\">Docker\u2019s docs on <strong><code>run<\/code><\/strong><\/a>, here, for more help. For our purposes here, it\u2019s much easier to reference by name, rather than a really long ID like \u201c0801f5872da47202beabb4aa122a2a5beeed6cf39759e312a0a67d225025b049\u201d<\/p>\n<p>To do that, it\u2019s a matter of finding the container name, which you can do by:<\/p>\n<p><code>$ docker ps<br \/>\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br \/>\nf6e9a636f8c2 owasp\/zap2docker-stable \"zap.sh -daemon -port\" About a minute ago Up About a minute 0.0.0.0:8090-&gt;8090\/tcp determined_pike<\/code><\/p>\n<p>Here, we can see that CONTAINER ID is f6e9a636f8c2 and NAMES gives us determined_pike<\/p>\n<p>So we can be sure ZAP is accessible outside of the Docker container, we follow the advice and commands from <a href=\"https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker#accessing-the-api-from-outside-of-the-docker-container\">https:\/\/github.com\/zaproxy\/zaproxy\/wiki\/Docker#accessing-the-api-from-outside-of-the-docker-container<\/a> which tells us to pass in -host 0.0.0.0<\/p>\n<p>For now, we also don\u2019t have nor need an API key, so following the last section of <a href=\"https:\/\/github.com\/Grunny\/zap-cli#extra-start-options\">https:\/\/github.com\/Grunny\/zap-cli#extra-start-options<\/a>, we set <code>-config.api-disablekey=true<\/code><\/p>\n<p><code>$ docker run -u zap -p 8090:8090 -d owasp\/zap2docker-stable zap.sh -daemon -port 8090 -host 0.0.0.0 -config api.disablekey=true<\/code><\/p>\n<p>Here, we\u2019re running as the \u201czap\u201d user, rather than Docker\u2019s default user, which is the root.<\/p>\n<p>After issuing this command, you should see a long dynamically-generated container ID, like so:<br \/>\nDfc68c6c9880a02d0242aece2afdbd083c0ac9bac99e4809a903aae458ef5436<\/p>\n<p>Finally, we can reference the Docker ZAP container by either its name or its container ID, and here I\u2019ll choose to use its Docker-generated name, which is <code>determined_pike<\/code><\/p>\n<p><code>$ docker exec determined_pike zap-cli open-url 'https:\/\/www.allizom.org'<br \/>\n[INFO] Accessing URL https:\/\/www.allizom.org<\/code><\/p>\n<p><code>$ docker ps<br \/>\nCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES<br \/>\nac68d9afcfad owasp\/zap2docker-stable \"zap.sh -daemon -port\" 29 seconds ago Up 28 seconds 0.0.0.0:8090-&gt;8090\/tcp determined_pike<\/code><\/p>\n<p><code>$ docker exec determined_pike zap-cli active-scan 'https:\/\/www.allizom.org'<br \/>\n[INFO] Running an active scan...<\/code><\/p>\n<p><code>$ docker logs determined_pike<br \/>\nFound Java version 1.7.0_91<br \/>\nAvailable memory: 2002 MB<br \/>\nSetting jvm heap size: -Xmx512m<br \/>\n275 [main] INFO org.zaproxy.zap.DaemonBootstrap - OWASP ZAP 2.4.3 started.<br \/>\n701 [main] INFO hsqldb.db.HSQLDB379AF3DEBD.ENGINE - dataFileCache open start<br \/>\n707 [main] INFO hsqldb.db.HSQLDB379AF3DEBD.ENGINE - dataFileCache open end<br \/>\n1108 [main] INFO org.parosproxy.paros.common.AbstractParam - Setting config api.disablekey = true was null<br \/>\n1111 [main] INFO org.parosproxy.paros.network.SSLConnector - Reading supported SSL\/TLS protocols...<br \/>\n1112 [main] INFO org.parosproxy.paros.network.SSLConnector - Using a SSLEngine...<br \/>\n1360 [main] INFO org.parosproxy.paros.network.SSLConnector - Done reading supported SSL\/TLS protocols: [SSLv2Hello, SSLv3, TLSv1, TLSv1.1, TLSv1.2]<br \/>\n1367 [main] INFO org.parosproxy.paros.extension.option.OptionsParamCertificate - Unsafe SSL renegotiation disabled.<br \/>\n1376 [ZAP-daemon] INFO org.zaproxy.zap.control.ExtensionFactory - Loading extensions<br \/>\n2332 [ZAP-daemon] INFO org.zaproxy.zap.control.ExtensionFactory - Extensions loaded<\/code><\/p>\n<p>Snipped many lines, here&#8230;<\/p>\n<p><code>141243 [Thread-9] INFO org.parosproxy.paros.core.scanner.HostProcess - start host https:\/\/www.allizom.org | TestPersistentXSSSpider strength MEDIUM threshold MEDIUM<br \/>\n141262 [Thread-9] INFO org.parosproxy.paros.core.scanner.HostProcess - completed host\/plugin https:\/\/www.allizom.org | TestPersistentXSSSpider in 0.019s<br \/>\n141263 [Thread-9] INFO org.parosproxy.paros.core.scanner.HostProcess - start host https:\/\/www.allizom.org | TestPersistentXSSAttack strength MEDIUM threshold MEDIUM<br \/>\n141264 [Thread-9] INFO org.parosproxy.paros.core.scanner.HostProcess - completed host\/plugin https:\/\/www.allizom.org | TestPersistentXSSAttack in 0.002s<br \/>\n141265 [Thread-9] INFO org.parosproxy.paros.core.scanner.HostProcess - start host https:\/\/www.allizom.org | ScriptsActiveScanner strength MEDIUM threshold MEDIUM<br \/>\n141266 [Thread-9] INFO org.parosproxy.paros.core.scanner.HostProcess - completed host\/plugin https:\/\/www.allizom.org | ScriptsActiveScanner in 0.002s<br \/>\n141266 [Thread-9] INFO org.parosproxy.paros.core.scanner.HostProcess - completed host https:\/\/www.allizom.org in 1.184s<br \/>\n141267 [Thread-8] INFO org.parosproxy.paros.core.scanner.Scanner - scanner completed in 1.216s<\/code><\/p>\n<p>While it\u2019s complete, having *just* the log output doesn\u2019t tell us actionable items &#8211; at least, not in an easy-to-read way.<\/p>\n<p>So, that\u2019s next on our list of many things to do! See the follow-up blog posts, coming within the next couple of weeks, where I aim to address this, and the <a href=\"https:\/\/github.com\/stephendonner\/docker-zap\/issues?utf8=%E2%9C%93&amp;q=is%3Aissue\">rest of the issues I\u2019ve raised<\/a>, and will be raising in my GitHub repo: <a href=\"https:\/\/github.com\/stephendonner\/docker-zap\">https:\/\/github.com\/stephendonner\/docker-zap<\/a><\/p>\n<p>Until then!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For a very long time, I\u2019ve been interested in automated security testing. Alan Parkinson\u2019s \u201cAutomated Security Testing\u201d presentation at Selenium Conference 2012 really highlighted the possibilities, for me. Since then, &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/fxtesteng\/2016\/05\/11\/docker-owasp-zap-part-one\/\">Read more<\/a><\/p>\n","protected":false},"author":512,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[8630,228,31343,278094,31342,706,69,278096],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/posts\/486"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/users\/512"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/comments?post=486"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/posts\/486\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/media?parent=486"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/categories?post=486"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/fxtesteng\/wp-json\/wp\/v2\/tags?post=486"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}