├── .gitignore ├── README.md ├── burp ├── Dockerfile ├── conf │ └── burp.config ├── entrypoint.sh ├── keys │ └── .gitkeep ├── pkg │ └── .gitkeep └── run.sh ├── certbot ├── certbot-dns-burp │ ├── Dockerfile │ ├── LICENSE.txt │ ├── MANIFEST.in │ ├── README.rst │ ├── certbot_dns_cloudflare │ │ ├── __init__.py │ │ ├── dns_cloudflare.py │ │ └── dns_cloudflare_test.py │ ├── docs │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── api.rst │ │ ├── api │ │ │ └── dns_cloudflare.rst │ │ ├── conf.py │ │ ├── index.rst │ │ └── make.bat │ ├── local-oldest-requirements.txt │ ├── readthedocs.org.requirements.txt │ ├── setup.cfg │ └── setup.py ├── certificaterenewal.sh ├── letsencrypt │ └── .gitkeep ├── logs │ └── .gitkeep ├── new.sh └── renew.sh └── init.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /burp/pkg/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Burp Collaborator Server docker container with LetsEncrypt certificate 2 | 3 | This repository includes a set of scripts to install a Burp Collaborator Server in a docker environment, using a LetsEncrypt wildcard certificate. 4 | The objective is to simplify as much as possible the process of setting up and maintaining the server. 5 | 6 | ## Setup your domain 7 | Delegate a domain or subdomain to your soon-to-be burp collaborator server IP address. At the minimum you'll need an NS record for the domain/subdomain to be used. 8 | 9 | For example, if your collaborator domain is `burpserver.example`, you need to make NS records pointing with an A record to the public IP of the server: `1.2.3.4` 10 | 11 | Here as an example `dig` command to confirm: 12 | ```bash 13 | dig NS burpserver.example 14 | 15 | Output: 16 | ; <<>> DiG 9.10.6 <<>> NS burpserver.example 17 | ;; global options: +cmd 18 | ;; Got answer: 19 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49449 20 | ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3 21 | 22 | ;; OPT PSEUDOSECTION: 23 | ; EDNS: version: 0, flags:; udp: 4000 24 | ;; QUESTION SECTION: 25 | ;burpserver.example. IN NS 26 | 27 | ;; ANSWER SECTION: 28 | burpserver.example. 308 IN NS ns2.burpserver.example. 29 | burpserver.example. 308 IN NS ns1.burpserver.example. 30 | 31 | ;; ADDITIONAL SECTION: 32 | ns2.burpserver.example. 308 IN A 1.2.3.4 33 | ns1.burpserver.example. 308 IN A 1.2.3.4 34 | 35 | ;; Query time: 52 msec 36 | ;; SERVER: 8.8.8.8#53(8.8.8.8) 37 | ;; WHEN: Fri Jul 12 11:20:29 EDT 2024 38 | ;; MSG SIZE rcvd: 104 39 | ``` 40 | 41 | Check https://portswigger.net/burp/documentation/collaborator/deploying#dns-configuration for further info. 42 | 43 | ## Requirements 44 | 45 | * Internet accessible server 46 | * bash 47 | * docker 48 | * bc 49 | * openssl 50 | * Burp Suite Professional 51 | 52 | ## Setup the environment 53 | 54 | * Clone or download the repository to the server (tested on ubuntu 16.04) to a directory of your choice. 55 | * Put the Burp Suite JAR file in ```./burp/pkg/burp.jar``` (make sure the name is exactly ```burp.jar```, and it is the actual file **not a link**) 56 | * Run init.sh with your subdomain and server public IP address as argument: 57 | 58 | ```./init.sh burp.example.com 1.2.3.4``` 59 | 60 | This will start the environment for the subdomain ```burp.example.com```, creating a wildcard certificate as ```*.burp.example.com```. 61 | 62 | I'm using an ugly hack on the certbot-dns-cloudflare plugin from certbot, where it just runs a local dnsmasq with the required records, and makes 63 | all of this automagically happen. 64 | 65 | If everything is OK, burp will start with the following message: 66 | 67 | > Burp is now running with the letsencrypt certificate for domain *.burp.example.com 68 | 69 | You can check by running ```docker ps```, and going to burp, and pointing the collaborator configuration to your new server. 70 | Keep it mind that this configuration configures the *polling server on port 9443*. 71 | 72 | The init.sh script will be renamed and disabled, so no accidents may happen. 73 | 74 | ## Certificate renewal 75 | 76 | * There's a renewal script in ```./certbot/certificaterenewal.sh```. When run, it renews the certificate if it expires in 30 days or less; 77 | * Optionally, edit the RENEWDAYS variable if you wish to. By default it will renew the certificate every 60 days. *If you want to force the renewal to check if everything is working, just set it to 89 days, and run it manually. Remember to set it back to 60 afterwards.*; 78 | * Set your crontab to run this script once a day. 79 | 80 | ## Updating Burp Suite 81 | 82 | * Download it and make sure you put it in ```./burp/pkg/burp.jar``` 83 | * Restart the container with ```docker restart burp``` 84 | 85 | ## Docker and UFW 86 | If you use UFW/IPTables as your firewall on the host, both UFW and docker modify the same [iptables](https://en.wikipedia.org/wiki/Iptables "iptables") configurations. Whatever UFW rules you have set, running a docker container completely ignores them and allows traffic, regardless of whether you explicitly block access. In order to fix the issue and be able to use UFW properly with docker, read this: 87 | 88 | https://blog.jarrousse.org/2023/03/18/how-to-use-ufw-firewall-with-docker-containers/ 89 | 90 | These instructions assume you have the default docker set up and didn't try to fix the problem yourself yet. 91 | **Download `ufw-docker` script** 92 | ```bash 93 | sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker 94 | sudo chmod +x /usr/local/bin/ufw-docker 95 | ``` 96 | 97 | Then using the following command to modify the `after.rules` file of `ufw` 98 | ```bash 99 | ufw-docker install 100 | ``` 101 | 102 | reboot the host and check if you can access the ports of your container. 103 | 104 | Now allow the traffic to the ports on the containers 105 | - Use the actual port thats open on the container, not the one its binded to on the host 106 | - `burp` is the container name, so thats what we use with below command 107 | ```bash 108 | docker ps -a 109 | sudo ufw-docker allow burp 8443 110 | ``` 111 | Pasted image 20240713201717 112 | 113 | I have provided the commands conventiently for you here: 114 | ```bash 115 | sudo ufw-docker allow burp 8053 116 | sudo ufw-docker allow burp 8053/udp 117 | sudo ufw-docker allow burp 8080 118 | sudo ufw-docker allow burp 8443 119 | sudo ufw-docker allow burp 8465 120 | sudo ufw-docker allow burp 8587 121 | sudo ufw-docker allow burp 8080 122 | ``` 123 | 124 | I HIGHLY recommend restricting access to your polling port from an IP address or network. Don't allow the general internet to use your burp collab server for free! 125 | - `your_whitelisted_ip` is your public IP to allow access from 126 | - `your_containers_local_ip` is 172.x.x.x 127 | 128 | ```bash 129 | ufw route allow proto tcp from your_whitelisted_ip to your_containers_local_ip port 9443 130 | ``` 131 | 132 | You should be good to go and have your UFW locked down! 133 | 134 | --- 135 | **Author:** [Bruno Morisson](https://twitter.com/morisson) 136 | 137 | Thanks to [Fábio Pires](https://twitter.com/fabiopirespt) (check his burp collaborator w/letsencrypt [tutorial](https://blog.fabiopires.pt/running-your-instance-of-burp-collaborator-server/)) and [Herman Duarte](https://twitter.com/hdontwit) (for betatesting and fixes) 138 | 139 | 140 | -------------------------------------------------------------------------------- /burp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye-slim 2 | 3 | RUN apt-get update && \ 4 | apt-get -yqq dist-upgrade 5 | 6 | # Install wget to download JDK 7 | RUN apt-get -yqq install wget && \ 8 | apt-get autoremove -yqq && \ 9 | apt-get clean && \ 10 | /bin/rm -rf /var/lib/apt/lists/* 11 | 12 | # Download and install Oracle JDK 21 13 | RUN wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.deb && \ 14 | dpkg -i jdk-21_linux-x64_bin.deb && \ 15 | rm jdk-21_linux-x64_bin.deb 16 | 17 | # Create a user and group for Burp 18 | RUN groupadd -g 999 burp && \ 19 | useradd -r -u 999 -g burp -d /opt/burp burp 20 | 21 | # Switch to the burp user 22 | USER burp 23 | 24 | ADD entrypoint.sh /opt/burp/entrypoint.sh 25 | WORKDIR /opt/burp 26 | ENTRYPOINT ["/opt/burp/entrypoint.sh"] 27 | -------------------------------------------------------------------------------- /burp/conf/burp.config: -------------------------------------------------------------------------------- 1 | { 2 | "serverDomain" : "DOMAIN", 3 | "workerThreads" : 40, 4 | 5 | "eventCapture": { 6 | "localAddress" : ["0.0.0.0"], 7 | "publicAddress": ["IP"], 8 | 9 | "http": { 10 | "port" : 8080 11 | }, 12 | 13 | "https": { 14 | "port" : 8443, 15 | "certificateFiles" : [ 16 | "/opt/burp/keys/privkey.pem", 17 | "/opt/burp/keys/cert.pem", 18 | "/opt/burp/keys/fullchain.pem" 19 | ] 20 | }, 21 | 22 | "smtp": { 23 | "ports": [8025,8587] 24 | }, 25 | "smtps": { 26 | "port": 8465, 27 | "certificateFiles" : [ 28 | "/opt/burp/keys/privkey.pem", 29 | "/opt/burp/keys/cert.pem", 30 | "/opt/burp/keys/fullchain.pem" 31 | ] 32 | } 33 | }, 34 | 35 | "polling" : { 36 | "localAddress" : "0.0.0.0", 37 | "http": { 38 | "localAddress" : "0.0.0.0", 39 | "publicAddress": ["IP"], 40 | "port" : 9090 41 | }, 42 | 43 | "https": { 44 | "port" : 9443, 45 | "localAddress" : "0.0.0.0", 46 | "publicAddress": ["IP"], 47 | "certificateFiles" : [ 48 | "/opt/burp/keys/privkey.pem", 49 | "/opt/burp/keys/cert.pem", 50 | "/opt/burp/keys/fullchain.pem" 51 | ] 52 | } 53 | }, 54 | 55 | "metrics": { 56 | "path" : "jnaicmez8", 57 | "addressWhitelist" : ["127.0.0.1/32"] 58 | }, 59 | 60 | "dns": { 61 | "interfaces" : [{ 62 | "publicAddress": ["IP"], 63 | "localAddress":"0.0.0.0", 64 | "name":"ns1" 65 | }], 66 | "port" : 8053, 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /burp/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd /opt/burp 3 | exec java -jar /opt/burp/pkg/burp.jar --collaborator-server --collaborator-config=/opt/burp/conf/burp.config 4 | -------------------------------------------------------------------------------- /burp/keys/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devoteam-cybertrust/burpcollaborator-docker/385e70115dc5eb43e78c7d7e99bd8fed7f8119d2/burp/keys/.gitkeep -------------------------------------------------------------------------------- /burp/pkg/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devoteam-cybertrust/burpcollaborator-docker/385e70115dc5eb43e78c7d7e99bd8fed7f8119d2/burp/pkg/.gitkeep -------------------------------------------------------------------------------- /burp/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo Starting burp... && \ 3 | docker run -d --restart=always --name burp --hostname burp -p 53:8053 -p 53:8053/udp -p 80:8080 -p 443:8443 -p 25:8025 -p 587:8587 -p 465:8465 -p 9090:9090 -p 9443:9443 -v $PWD/burp/keys:/opt/burp/keys:ro -v $PWD/burp/conf:/opt/burp/conf:ro -v $PWD/burp/pkg:/opt/burp/pkg:ro burp && \ 4 | echo Done. 5 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM certbot/certbot 2 | 3 | COPY . src/certbot-dns-cloudflare 4 | 5 | RUN pip install --no-cache-dir --editable src/certbot-dns-cloudflare 6 | 7 | # INSTALL DNSMASQ 8 | RUN apk add dnsmasq 9 | RUN echo 'conf-dir=/etc/dnsmasq.d/,*.conf' > /etc/dnsmasq.conf 10 | RUN echo "user=root" >> /etc/dnsmasq.conf 11 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015 Electronic Frontier Foundation and others 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | Apache License 16 | Version 2.0, January 2004 17 | http://www.apache.org/licenses/ 18 | 19 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 20 | 21 | 1. Definitions. 22 | 23 | "License" shall mean the terms and conditions for use, reproduction, 24 | and distribution as defined by Sections 1 through 9 of this document. 25 | 26 | "Licensor" shall mean the copyright owner or entity authorized by 27 | the copyright owner that is granting the License. 28 | 29 | "Legal Entity" shall mean the union of the acting entity and all 30 | other entities that control, are controlled by, or are under common 31 | control with that entity. For the purposes of this definition, 32 | "control" means (i) the power, direct or indirect, to cause the 33 | direction or management of such entity, whether by contract or 34 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 35 | outstanding shares, or (iii) beneficial ownership of such entity. 36 | 37 | "You" (or "Your") shall mean an individual or Legal Entity 38 | exercising permissions granted by this License. 39 | 40 | "Source" form shall mean the preferred form for making modifications, 41 | including but not limited to software source code, documentation 42 | source, and configuration files. 43 | 44 | "Object" form shall mean any form resulting from mechanical 45 | transformation or translation of a Source form, including but 46 | not limited to compiled object code, generated documentation, 47 | and conversions to other media types. 48 | 49 | "Work" shall mean the work of authorship, whether in Source or 50 | Object form, made available under the License, as indicated by a 51 | copyright notice that is included in or attached to the work 52 | (an example is provided in the Appendix below). 53 | 54 | "Derivative Works" shall mean any work, whether in Source or Object 55 | form, that is based on (or derived from) the Work and for which the 56 | editorial revisions, annotations, elaborations, or other modifications 57 | represent, as a whole, an original work of authorship. For the purposes 58 | of this License, Derivative Works shall not include works that remain 59 | separable from, or merely link (or bind by name) to the interfaces of, 60 | the Work and Derivative Works thereof. 61 | 62 | "Contribution" shall mean any work of authorship, including 63 | the original version of the Work and any modifications or additions 64 | to that Work or Derivative Works thereof, that is intentionally 65 | submitted to Licensor for inclusion in the Work by the copyright owner 66 | or by an individual or Legal Entity authorized to submit on behalf of 67 | the copyright owner. For the purposes of this definition, "submitted" 68 | means any form of electronic, verbal, or written communication sent 69 | to the Licensor or its representatives, including but not limited to 70 | communication on electronic mailing lists, source code control systems, 71 | and issue tracking systems that are managed by, or on behalf of, the 72 | Licensor for the purpose of discussing and improving the Work, but 73 | excluding communication that is conspicuously marked or otherwise 74 | designated in writing by the copyright owner as "Not a Contribution." 75 | 76 | "Contributor" shall mean Licensor and any individual or Legal Entity 77 | on behalf of whom a Contribution has been received by Licensor and 78 | subsequently incorporated within the Work. 79 | 80 | 2. Grant of Copyright License. Subject to the terms and conditions of 81 | this License, each Contributor hereby grants to You a perpetual, 82 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 83 | copyright license to reproduce, prepare Derivative Works of, 84 | publicly display, publicly perform, sublicense, and distribute the 85 | Work and such Derivative Works in Source or Object form. 86 | 87 | 3. Grant of Patent License. Subject to the terms and conditions of 88 | this License, each Contributor hereby grants to You a perpetual, 89 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 90 | (except as stated in this section) patent license to make, have made, 91 | use, offer to sell, sell, import, and otherwise transfer the Work, 92 | where such license applies only to those patent claims licensable 93 | by such Contributor that are necessarily infringed by their 94 | Contribution(s) alone or by combination of their Contribution(s) 95 | with the Work to which such Contribution(s) was submitted. If You 96 | institute patent litigation against any entity (including a 97 | cross-claim or counterclaim in a lawsuit) alleging that the Work 98 | or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses 100 | granted to You under this License for that Work shall terminate 101 | as of the date such litigation is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the 104 | Work or Derivative Works thereof in any medium, with or without 105 | modifications, and in Source or Object form, provided that You 106 | meet the following conditions: 107 | 108 | (a) You must give any other recipients of the Work or 109 | Derivative Works a copy of this License; and 110 | 111 | (b) You must cause any modified files to carry prominent notices 112 | stating that You changed the files; and 113 | 114 | (c) You must retain, in the Source form of any Derivative Works 115 | that You distribute, all copyright, patent, trademark, and 116 | attribution notices from the Source form of the Work, 117 | excluding those notices that do not pertain to any part of 118 | the Derivative Works; and 119 | 120 | (d) If the Work includes a "NOTICE" text file as part of its 121 | distribution, then any Derivative Works that You distribute must 122 | include a readable copy of the attribution notices contained 123 | within such NOTICE file, excluding those notices that do not 124 | pertain to any part of the Derivative Works, in at least one 125 | of the following places: within a NOTICE text file distributed 126 | as part of the Derivative Works; within the Source form or 127 | documentation, if provided along with the Derivative Works; or, 128 | within a display generated by the Derivative Works, if and 129 | wherever such third-party notices normally appear. The contents 130 | of the NOTICE file are for informational purposes only and 131 | do not modify the License. You may add Your own attribution 132 | notices within Derivative Works that You distribute, alongside 133 | or as an addendum to the NOTICE text from the Work, provided 134 | that such additional attribution notices cannot be construed 135 | as modifying the License. 136 | 137 | You may add Your own copyright statement to Your modifications and 138 | may provide additional or different license terms and conditions 139 | for use, reproduction, or distribution of Your modifications, or 140 | for any such Derivative Works as a whole, provided Your use, 141 | reproduction, and distribution of the Work otherwise complies with 142 | the conditions stated in this License. 143 | 144 | 5. Submission of Contributions. Unless You explicitly state otherwise, 145 | any Contribution intentionally submitted for inclusion in the Work 146 | by You to the Licensor shall be under the terms and conditions of 147 | this License, without any additional terms or conditions. 148 | Notwithstanding the above, nothing herein shall supersede or modify 149 | the terms of any separate license agreement you may have executed 150 | with Licensor regarding such Contributions. 151 | 152 | 6. Trademarks. This License does not grant permission to use the trade 153 | names, trademarks, service marks, or product names of the Licensor, 154 | except as required for reasonable and customary use in describing the 155 | origin of the Work and reproducing the content of the NOTICE file. 156 | 157 | 7. Disclaimer of Warranty. Unless required by applicable law or 158 | agreed to in writing, Licensor provides the Work (and each 159 | Contributor provides its Contributions) on an "AS IS" BASIS, 160 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 161 | implied, including, without limitation, any warranties or conditions 162 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 163 | PARTICULAR PURPOSE. You are solely responsible for determining the 164 | appropriateness of using or redistributing the Work and assume any 165 | risks associated with Your exercise of permissions under this License. 166 | 167 | 8. Limitation of Liability. In no event and under no legal theory, 168 | whether in tort (including negligence), contract, or otherwise, 169 | unless required by applicable law (such as deliberate and grossly 170 | negligent acts) or agreed to in writing, shall any Contributor be 171 | liable to You for damages, including any direct, indirect, special, 172 | incidental, or consequential damages of any character arising as a 173 | result of this License or out of the use or inability to use the 174 | Work (including but not limited to damages for loss of goodwill, 175 | work stoppage, computer failure or malfunction, or any and all 176 | other commercial damages or losses), even if such Contributor 177 | has been advised of the possibility of such damages. 178 | 179 | 9. Accepting Warranty or Additional Liability. While redistributing 180 | the Work or Derivative Works thereof, You may choose to offer, 181 | and charge a fee for, acceptance of support, warranty, indemnity, 182 | or other liability obligations and/or rights consistent with this 183 | License. However, in accepting such obligations, You may act only 184 | on Your own behalf and on Your sole responsibility, not on behalf 185 | of any other Contributor, and only if You agree to indemnify, 186 | defend, and hold each Contributor harmless for any liability 187 | incurred by, or claims asserted against, such Contributor by reason 188 | of your accepting any such warranty or additional liability. 189 | 190 | END OF TERMS AND CONDITIONS 191 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include README.rst 3 | recursive-include docs * 4 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/README.rst: -------------------------------------------------------------------------------- 1 | Cloudflare DNS Authenticator plugin for Certbot 2 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/certbot_dns_cloudflare/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The `~certbot_dns_cloudflare.dns_cloudflare` plugin automates the process of 3 | completing a ``dns-01`` challenge (`~acme.challenges.DNS01`) by creating, and 4 | subsequently removing, TXT records using the Cloudflare API. 5 | 6 | 7 | Named Arguments 8 | --------------- 9 | 10 | ======================================== ===================================== 11 | ``--dns-cloudflare-credentials`` Cloudflare credentials_ INI file. 12 | (Required) 13 | ``--dns-cloudflare-propagation-seconds`` The number of seconds to wait for DNS 14 | to propagate before asking the ACME 15 | server to verify the DNS record. 16 | (Default: 10) 17 | ======================================== ===================================== 18 | 19 | 20 | Credentials 21 | ----------- 22 | 23 | Use of this plugin requires a configuration file containing Cloudflare API 24 | credentials, obtained from your Cloudflare 25 | `account page `_. 26 | 27 | .. code-block:: ini 28 | :name: credentials.ini 29 | :caption: Example credentials file: 30 | 31 | # Cloudflare API credentials used by Certbot 32 | dns_cloudflare_email = cloudflare@example.com 33 | dns_cloudflare_api_key = 0123456789abcdef0123456789abcdef01234567 34 | 35 | The path to this file can be provided interactively or using the 36 | ``--dns-cloudflare-credentials`` command-line argument. Certbot records the path 37 | to this file for use during renewal, but does not store the file's contents. 38 | 39 | .. caution:: 40 | You should protect these API credentials as you would the password to your 41 | Cloudflare account. Users who can read this file can use these credentials 42 | to issue arbitrary API calls on your behalf. Users who can cause Certbot to 43 | run using these credentials can complete a ``dns-01`` challenge to acquire 44 | new certificates or revoke existing certificates for associated domains, 45 | even if those domains aren't being managed by this server. 46 | 47 | Certbot will emit a warning if it detects that the credentials file can be 48 | accessed by other users on your system. The warning reads "Unsafe permissions 49 | on credentials configuration file", followed by the path to the credentials 50 | file. This warning will be emitted each time Certbot uses the credentials file, 51 | including for renewal, and cannot be silenced except by addressing the issue 52 | (e.g., by using a command like ``chmod 600`` to restrict access to the file). 53 | 54 | 55 | Examples 56 | -------- 57 | 58 | .. code-block:: bash 59 | :caption: To acquire a certificate for ``example.com`` 60 | 61 | certbot certonly \\ 62 | --dns-cloudflare \\ 63 | --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \\ 64 | -d example.com 65 | 66 | .. code-block:: bash 67 | :caption: To acquire a single certificate for both ``example.com`` and 68 | ``www.example.com`` 69 | 70 | certbot certonly \\ 71 | --dns-cloudflare \\ 72 | --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \\ 73 | -d example.com \\ 74 | -d www.example.com 75 | 76 | .. code-block:: bash 77 | :caption: To acquire a certificate for ``example.com``, waiting 60 seconds 78 | for DNS propagation 79 | 80 | certbot certonly \\ 81 | --dns-cloudflare \\ 82 | --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \\ 83 | --dns-cloudflare-propagation-seconds 60 \\ 84 | -d example.com 85 | 86 | """ 87 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/certbot_dns_cloudflare/dns_cloudflare.py: -------------------------------------------------------------------------------- 1 | """DNS Authenticator for Cloudflare.""" 2 | """ Ugly hack to support burp collaborator certificates using dnsmasq 3 | IT DOES NOT WORK WITH CLOUDFLARE!!!! 4 | """ 5 | import logging 6 | 7 | #import CloudFlare 8 | import zope.interface 9 | 10 | from certbot import errors 11 | from certbot import interfaces 12 | from certbot.plugins import dns_common 13 | 14 | import subprocess 15 | 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | 20 | @zope.interface.implementer(interfaces.IAuthenticator) 21 | @zope.interface.provider(interfaces.IPluginFactory) 22 | class Authenticator(dns_common.DNSAuthenticator): 23 | """DNS Authenticator for Cloudflare 24 | 25 | This Authenticator uses the Cloudflare API to fulfill a dns-01 challenge. 26 | """ 27 | 28 | description = ('Obtain certificates using a DNS TXT record (if you are using Cloudflare for ' 29 | 'DNS).') 30 | ttl = 120 31 | chall01 = None 32 | chall01_vn = None 33 | chall02 = None 34 | chall02_vn = None 35 | 36 | 37 | def __init__(self, *args, **kwargs): 38 | super(Authenticator, self).__init__(*args, **kwargs) 39 | self.chall01 = None 40 | self.chall01_vn = None 41 | self.chall02 = None 42 | self.chall02_vn = None 43 | 44 | def more_info(self): # pylint: disable=missing-docstring,no-self-use 45 | return 'This plugin configures a DNS TXT record to respond to a dns-01 challenge using ' + \ 46 | 'the Cloudflare API.' 47 | 48 | def _setup_credentials(self): 49 | return True 50 | 51 | def _perform(self, domain, validation_name, validation): 52 | self.add_txt_record(domain, validation_name, validation) 53 | 54 | def _cleanup(self, domain, validation_name, validation): 55 | self.del_txt_record(domain, validation_name, validation) 56 | 57 | 58 | def add_txt_record(self,domain, validation_name, validation): 59 | if self.chall01: 60 | self.chall02=validation 61 | self.chall02_vn=validation_name 62 | print("Launching DNSMASQ...") 63 | self.launch_dnsmasq(domain) 64 | else: 65 | self.chall01=validation 66 | self.chall01_vn=validation_name 67 | 68 | def del_txt_record(self,domain, validation_name, validation): 69 | return True 70 | 71 | def launch_dnsmasq(self,domain): 72 | dnsmasq='/usr/sbin/dnsmasq -q --dns-rr='+domain+',257,000569737375656C657473656E63727970742E6F7267 --txt-record='+self.chall01_vn+',"'+self.chall01+'" --txt-record='+self.chall02_vn+',"'+self.chall02+'" --no-resolv --port=53' 73 | print("DNSMASQ CMD: \n{}".format(dnsmasq)) 74 | subprocess.Popen(dnsmasq, shell=True, stdout=subprocess.PIPE) 75 | 76 | 77 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/certbot_dns_cloudflare/dns_cloudflare_test.py: -------------------------------------------------------------------------------- 1 | """Tests for certbot_dns_cloudflare.dns_cloudflare.""" 2 | 3 | import os 4 | import unittest 5 | 6 | import CloudFlare 7 | import mock 8 | 9 | from certbot import errors 10 | from certbot.plugins import dns_test_common 11 | from certbot.plugins.dns_test_common import DOMAIN 12 | from certbot.tests import util as test_util 13 | 14 | API_ERROR = CloudFlare.exceptions.CloudFlareAPIError(1000, '', '') 15 | API_KEY = 'an-api-key' 16 | EMAIL = 'example@example.com' 17 | 18 | 19 | class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthenticatorTest): 20 | 21 | def setUp(self): 22 | from certbot_dns_cloudflare.dns_cloudflare import Authenticator 23 | 24 | super(AuthenticatorTest, self).setUp() 25 | 26 | path = os.path.join(self.tempdir, 'file.ini') 27 | dns_test_common.write({"cloudflare_email": EMAIL, "cloudflare_api_key": API_KEY}, path) 28 | 29 | self.config = mock.MagicMock(cloudflare_credentials=path, 30 | cloudflare_propagation_seconds=0) # don't wait during tests 31 | 32 | self.auth = Authenticator(self.config, "cloudflare") 33 | 34 | self.mock_client = mock.MagicMock() 35 | # _get_cloudflare_client | pylint: disable=protected-access 36 | self.auth._get_cloudflare_client = mock.MagicMock(return_value=self.mock_client) 37 | 38 | def test_perform(self): 39 | self.auth.perform([self.achall]) 40 | 41 | expected = [mock.call.add_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)] 42 | self.assertEqual(expected, self.mock_client.mock_calls) 43 | 44 | def test_cleanup(self): 45 | # _attempt_cleanup | pylint: disable=protected-access 46 | self.auth._attempt_cleanup = True 47 | self.auth.cleanup([self.achall]) 48 | 49 | expected = [mock.call.del_txt_record(DOMAIN, '_acme-challenge.'+DOMAIN, mock.ANY)] 50 | self.assertEqual(expected, self.mock_client.mock_calls) 51 | 52 | 53 | class CloudflareClientTest(unittest.TestCase): 54 | record_name = "foo" 55 | record_content = "bar" 56 | record_ttl = 42 57 | zone_id = 1 58 | record_id = 2 59 | 60 | def setUp(self): 61 | from certbot_dns_cloudflare.dns_cloudflare import _CloudflareClient 62 | 63 | self.cloudflare_client = _CloudflareClient(EMAIL, API_KEY) 64 | 65 | self.cf = mock.MagicMock() 66 | self.cloudflare_client.cf = self.cf 67 | 68 | def test_add_txt_record(self): 69 | self.cf.zones.get.return_value = [{'id': self.zone_id}] 70 | 71 | self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content, 72 | self.record_ttl) 73 | 74 | self.cf.zones.dns_records.post.assert_called_with(self.zone_id, data=mock.ANY) 75 | 76 | post_data = self.cf.zones.dns_records.post.call_args[1]['data'] 77 | 78 | self.assertEqual('TXT', post_data['type']) 79 | self.assertEqual(self.record_name, post_data['name']) 80 | self.assertEqual(self.record_content, post_data['content']) 81 | self.assertEqual(self.record_ttl, post_data['ttl']) 82 | 83 | def test_add_txt_record_error(self): 84 | self.cf.zones.get.return_value = [{'id': self.zone_id}] 85 | 86 | self.cf.zones.dns_records.post.side_effect = API_ERROR 87 | 88 | self.assertRaises( 89 | errors.PluginError, 90 | self.cloudflare_client.add_txt_record, 91 | DOMAIN, self.record_name, self.record_content, self.record_ttl) 92 | 93 | def test_add_txt_record_error_during_zone_lookup(self): 94 | self.cf.zones.get.side_effect = API_ERROR 95 | 96 | self.assertRaises( 97 | errors.PluginError, 98 | self.cloudflare_client.add_txt_record, 99 | DOMAIN, self.record_name, self.record_content, self.record_ttl) 100 | 101 | def test_add_txt_record_zone_not_found(self): 102 | self.cf.zones.get.return_value = [] 103 | 104 | self.assertRaises( 105 | errors.PluginError, 106 | self.cloudflare_client.add_txt_record, 107 | DOMAIN, self.record_name, self.record_content, self.record_ttl) 108 | 109 | def test_del_txt_record(self): 110 | self.cf.zones.get.return_value = [{'id': self.zone_id}] 111 | self.cf.zones.dns_records.get.return_value = [{'id': self.record_id}] 112 | 113 | self.cloudflare_client.del_txt_record(DOMAIN, self.record_name, self.record_content) 114 | 115 | expected = [mock.call.zones.get(params=mock.ANY), 116 | mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY), 117 | mock.call.zones.dns_records.delete(self.zone_id, self.record_id)] 118 | 119 | self.assertEqual(expected, self.cf.mock_calls) 120 | 121 | get_data = self.cf.zones.dns_records.get.call_args[1]['params'] 122 | 123 | self.assertEqual('TXT', get_data['type']) 124 | self.assertEqual(self.record_name, get_data['name']) 125 | self.assertEqual(self.record_content, get_data['content']) 126 | 127 | def test_del_txt_record_error_during_zone_lookup(self): 128 | self.cf.zones.get.side_effect = API_ERROR 129 | 130 | self.cloudflare_client.del_txt_record(DOMAIN, self.record_name, self.record_content) 131 | 132 | def test_del_txt_record_error_during_delete(self): 133 | self.cf.zones.get.return_value = [{'id': self.zone_id}] 134 | self.cf.zones.dns_records.get.return_value = [{'id': self.record_id}] 135 | self.cf.zones.dns_records.delete.side_effect = API_ERROR 136 | 137 | self.cloudflare_client.del_txt_record(DOMAIN, self.record_name, self.record_content) 138 | expected = [mock.call.zones.get(params=mock.ANY), 139 | mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY), 140 | mock.call.zones.dns_records.delete(self.zone_id, self.record_id)] 141 | 142 | self.assertEqual(expected, self.cf.mock_calls) 143 | 144 | def test_del_txt_record_error_during_get(self): 145 | self.cf.zones.get.return_value = [{'id': self.zone_id}] 146 | self.cf.zones.dns_records.get.side_effect = API_ERROR 147 | 148 | self.cloudflare_client.del_txt_record(DOMAIN, self.record_name, self.record_content) 149 | expected = [mock.call.zones.get(params=mock.ANY), 150 | mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY)] 151 | 152 | self.assertEqual(expected, self.cf.mock_calls) 153 | 154 | def test_del_txt_record_no_record(self): 155 | self.cf.zones.get.return_value = [{'id': self.zone_id}] 156 | self.cf.zones.dns_records.get.return_value = [] 157 | 158 | self.cloudflare_client.del_txt_record(DOMAIN, self.record_name, self.record_content) 159 | expected = [mock.call.zones.get(params=mock.ANY), 160 | mock.call.zones.dns_records.get(self.zone_id, params=mock.ANY)] 161 | 162 | self.assertEqual(expected, self.cf.mock_calls) 163 | 164 | def test_del_txt_record_no_zone(self): 165 | self.cf.zones.get.return_value = [{'id': None}] 166 | 167 | self.cloudflare_client.del_txt_record(DOMAIN, self.record_name, self.record_content) 168 | expected = [mock.call.zones.get(params=mock.ANY)] 169 | 170 | self.assertEqual(expected, self.cf.mock_calls) 171 | 172 | 173 | if __name__ == "__main__": 174 | unittest.main() # pragma: no cover 175 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/docs/.gitignore: -------------------------------------------------------------------------------- 1 | /_build/ 2 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = certbot-dns-cloudflare 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/docs/api.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | API Documentation 3 | ================= 4 | 5 | .. toctree:: 6 | :glob: 7 | 8 | api/** 9 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/docs/api/dns_cloudflare.rst: -------------------------------------------------------------------------------- 1 | :mod:`certbot_dns_cloudflare.dns_cloudflare` 2 | -------------------------------------------- 3 | 4 | .. automodule:: certbot_dns_cloudflare.dns_cloudflare 5 | :members: 6 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # certbot-dns-cloudflare documentation build configuration file, created by 4 | # sphinx-quickstart on Tue May 9 10:20:04 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ['sphinx.ext.autodoc', 34 | 'sphinx.ext.intersphinx', 35 | 'sphinx.ext.todo', 36 | 'sphinx.ext.coverage', 37 | 'sphinx.ext.viewcode'] 38 | 39 | autodoc_member_order = 'bysource' 40 | autodoc_default_flags = ['show-inheritance', 'private-members'] 41 | 42 | # Add any paths that contain templates here, relative to this directory. 43 | templates_path = ['_templates'] 44 | 45 | # The suffix(es) of source filenames. 46 | # You can specify multiple suffix as a list of string: 47 | # 48 | # source_suffix = ['.rst', '.md'] 49 | source_suffix = '.rst' 50 | 51 | # The master toctree document. 52 | master_doc = 'index' 53 | 54 | # General information about the project. 55 | project = u'certbot-dns-cloudflare' 56 | copyright = u'2017, Certbot Project' 57 | author = u'Certbot Project' 58 | 59 | # The version info for the project you're documenting, acts as replacement for 60 | # |version| and |release|, also used in various other places throughout the 61 | # built documents. 62 | # 63 | # The short X.Y version. 64 | version = u'0' 65 | # The full version, including alpha/beta/rc tags. 66 | release = u'0' 67 | 68 | # The language for content autogenerated by Sphinx. Refer to documentation 69 | # for a list of supported languages. 70 | # 71 | # This is also used if you do content translation via gettext catalogs. 72 | # Usually you set "language" from the command line for these cases. 73 | language = 'en' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | # This patterns also effect to html_static_path and html_extra_path 78 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 79 | 80 | default_role = 'py:obj' 81 | 82 | # The name of the Pygments (syntax highlighting) style to use. 83 | pygments_style = 'sphinx' 84 | 85 | # If true, `todo` and `todoList` produce output, else they produce nothing. 86 | todo_include_todos = True 87 | 88 | 89 | # -- Options for HTML output ---------------------------------------------- 90 | 91 | # The theme to use for HTML and HTML Help pages. See the documentation for 92 | # a list of builtin themes. 93 | # 94 | 95 | # http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs 96 | # on_rtd is whether we are on readthedocs.org 97 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 98 | if not on_rtd: # only import and set the theme if we're building docs locally 99 | import sphinx_rtd_theme 100 | html_theme = 'sphinx_rtd_theme' 101 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 102 | # otherwise, readthedocs.org uses their theme by default, so no need to specify it 103 | 104 | # Theme options are theme-specific and customize the look and feel of a theme 105 | # further. For a list of options available for each theme, see the 106 | # documentation. 107 | # 108 | # html_theme_options = {} 109 | 110 | # Add any paths that contain custom static files (such as style sheets) here, 111 | # relative to this directory. They are copied after the builtin static files, 112 | # so a file named "default.css" will overwrite the builtin "default.css". 113 | html_static_path = ['_static'] 114 | 115 | 116 | # -- Options for HTMLHelp output ------------------------------------------ 117 | 118 | # Output file base name for HTML help builder. 119 | htmlhelp_basename = 'certbot-dns-cloudflaredoc' 120 | 121 | 122 | # -- Options for LaTeX output --------------------------------------------- 123 | 124 | latex_elements = { 125 | # The paper size ('letterpaper' or 'a4paper'). 126 | # 127 | # 'papersize': 'letterpaper', 128 | 129 | # The font size ('10pt', '11pt' or '12pt'). 130 | # 131 | # 'pointsize': '10pt', 132 | 133 | # Additional stuff for the LaTeX preamble. 134 | # 135 | # 'preamble': '', 136 | 137 | # Latex figure (float) alignment 138 | # 139 | # 'figure_align': 'htbp', 140 | } 141 | 142 | # Grouping the document tree into LaTeX files. List of tuples 143 | # (source start file, target name, title, 144 | # author, documentclass [howto, manual, or own class]). 145 | latex_documents = [ 146 | (master_doc, 'certbot-dns-cloudflare.tex', u'certbot-dns-cloudflare Documentation', 147 | u'Certbot Project', 'manual'), 148 | ] 149 | 150 | 151 | # -- Options for manual page output --------------------------------------- 152 | 153 | # One entry per manual page. List of tuples 154 | # (source start file, name, description, authors, manual section). 155 | man_pages = [ 156 | (master_doc, 'certbot-dns-cloudflare', u'certbot-dns-cloudflare Documentation', 157 | [author], 1) 158 | ] 159 | 160 | 161 | # -- Options for Texinfo output ------------------------------------------- 162 | 163 | # Grouping the document tree into Texinfo files. List of tuples 164 | # (source start file, target name, title, author, 165 | # dir menu entry, description, category) 166 | texinfo_documents = [ 167 | (master_doc, 'certbot-dns-cloudflare', u'certbot-dns-cloudflare Documentation', 168 | author, 'certbot-dns-cloudflare', 'One line description of project.', 169 | 'Miscellaneous'), 170 | ] 171 | 172 | 173 | 174 | 175 | # Example configuration for intersphinx: refer to the Python standard library. 176 | intersphinx_mapping = { 177 | 'python': ('https://docs.python.org/', None), 178 | 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), 179 | 'certbot': ('https://certbot.eff.org/docs/', None), 180 | } 181 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/docs/index.rst: -------------------------------------------------------------------------------- 1 | .. certbot-dns-cloudflare documentation master file, created by 2 | sphinx-quickstart on Tue May 9 10:20:04 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to certbot-dns-cloudflare's documentation! 7 | ================================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | .. automodule:: certbot_dns_cloudflare 14 | :members: 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | 19 | api 20 | 21 | 22 | Indices and tables 23 | ================== 24 | 25 | * :ref:`genindex` 26 | * :ref:`modindex` 27 | * :ref:`search` 28 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=certbot-dns-cloudflare 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/local-oldest-requirements.txt: -------------------------------------------------------------------------------- 1 | acme[dev]==0.21.1 2 | certbot[dev]==0.21.1 3 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/readthedocs.org.requirements.txt: -------------------------------------------------------------------------------- 1 | # readthedocs.org gives no way to change the install command to "pip 2 | # install -e .[docs]" (that would in turn install documentation 3 | # dependencies), but it allows to specify a requirements.txt file at 4 | # https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) 5 | 6 | # Although ReadTheDocs certainly doesn't need to install the project 7 | # in --editable mode (-e), just "pip install .[docs]" does not work as 8 | # expected and "pip install -e .[docs]" must be used instead 9 | 10 | -e acme 11 | -e . 12 | -e certbot-dns-cloudflare[docs] 13 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /certbot/certbot-dns-burp/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from setuptools import find_packages 3 | 4 | 5 | version = '0.31.0.dev0' 6 | 7 | # Remember to update local-oldest-requirements.txt when changing the minimum 8 | # acme/certbot version. 9 | install_requires = [ 10 | 'acme>=0.21.1', 11 | 'certbot>=0.21.1', 12 | 'cloudflare>=1.5.1', 13 | 'mock', 14 | 'setuptools', 15 | 'zope.interface', 16 | ] 17 | 18 | docs_extras = [ 19 | 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 20 | 'sphinx_rtd_theme', 21 | ] 22 | 23 | setup( 24 | name='certbot-dns-cloudflare', 25 | version=version, 26 | description="Cloudflare DNS Authenticator plugin for Certbot", 27 | url='https://github.com/certbot/certbot', 28 | author="Certbot Project", 29 | author_email='client-dev@letsencrypt.org', 30 | license='Apache License 2.0', 31 | python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', 32 | classifiers=[ 33 | 'Development Status :: 3 - Alpha', 34 | 'Environment :: Plugins', 35 | 'Intended Audience :: System Administrators', 36 | 'License :: OSI Approved :: Apache Software License', 37 | 'Operating System :: POSIX :: Linux', 38 | 'Programming Language :: Python', 39 | 'Programming Language :: Python :: 2', 40 | 'Programming Language :: Python :: 2.7', 41 | 'Programming Language :: Python :: 3', 42 | 'Programming Language :: Python :: 3.4', 43 | 'Programming Language :: Python :: 3.5', 44 | 'Programming Language :: Python :: 3.6', 45 | 'Programming Language :: Python :: 3.7', 46 | 'Topic :: Internet :: WWW/HTTP', 47 | 'Topic :: Security', 48 | 'Topic :: System :: Installation/Setup', 49 | 'Topic :: System :: Networking', 50 | 'Topic :: System :: Systems Administration', 51 | 'Topic :: Utilities', 52 | ], 53 | 54 | packages=find_packages(), 55 | include_package_data=True, 56 | install_requires=install_requires, 57 | extras_require={ 58 | 'docs': docs_extras, 59 | }, 60 | entry_points={ 61 | 'certbot.plugins': [ 62 | 'dns-cloudflare = certbot_dns_cloudflare.dns_cloudflare:Authenticator', 63 | ], 64 | }, 65 | test_suite='certbot_dns_cloudflare', 66 | ) 67 | -------------------------------------------------------------------------------- /certbot/certificaterenewal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Renew certificates every X days 4 | # 5 | # requires openssl and bc 6 | # 7 | 8 | RENEWDAYS=30 9 | 10 | BASEDIR=__BASEDIR__ 11 | 12 | 13 | DOMAIN=__DOMAIN__ 14 | 15 | CURRENT=`/bin/date +%s` 16 | CERTIFICATE=`/usr/bin/openssl x509 -noout -dates -in $BASEDIR/certbot/letsencrypt/live/$DOMAIN/cert.pem | /bin/grep notAfter | /usr/bin/cut -d "=" -f 2` 17 | CERTDATE=`/bin/date -d "$CERTIFICATE" +%s` 18 | 19 | DAYS=`/bin/echo \($CERTDATE - $CURRENT\)/60/60/24 | /usr/bin/bc` 20 | 21 | if [ $DAYS -lt $RENEWDAYS ]; then 22 | 23 | echo Renewing certificate... 24 | # this may fail, we don't care 25 | docker stop burp 26 | docker rm burp 27 | cd $BASEDIR && \ 28 | ./certbot/renew.sh $DOMAIN && \ 29 | /bin/cp -r -f $BASEDIR/certbot/letsencrypt/live/$DOMAIN/ $BASEDIR/burp/keys && \ 30 | chown 999:999 $PWD/burp/keys/$DOMAIN/privkey.pem && \ 31 | ./burp/run.sh && \ 32 | echo Certificate renewed 33 | 34 | else 35 | 36 | echo Still $DAYS days to expire. Not renewing 37 | 38 | fi 39 | 40 | echo waiting for 5 seconds just to make sure all is ok! 41 | 42 | sleep 5 43 | 44 | if [ ! "$(docker ps -q -f name=burp)" ]; then 45 | if [ "$(docker ps -aq -f status=exited -f name=burp)" ]; then 46 | # cleanup 47 | docker rm burp 48 | fi 49 | # run your container 50 | #docker run -d --name my-docker-image 51 | $BASEDIR/burp/run.sh 52 | echo burp docker has been restarted 53 | else 54 | 55 | echo burp docker is running 56 | 57 | fi 58 | -------------------------------------------------------------------------------- /certbot/letsencrypt/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devoteam-cybertrust/burpcollaborator-docker/385e70115dc5eb43e78c7d7e99bd8fed7f8119d2/certbot/letsencrypt/.gitkeep -------------------------------------------------------------------------------- /certbot/logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devoteam-cybertrust/burpcollaborator-docker/385e70115dc5eb43e78c7d7e99bd8fed7f8119d2/certbot/logs/.gitkeep -------------------------------------------------------------------------------- /certbot/new.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -ne 1 ]; then 4 | echo usage: ./$0 \ 5 | exit 0 6 | fi 7 | 8 | DOMAIN=$1 9 | 10 | echo Running certbot for domain $DOMAIN 11 | echo 12 | read -p "Press any key to continue, or CTRL-C to bail out" var_p 13 | 14 | docker run --rm --name certbot --hostname certbot -ti -p 53:53/udp -p 53:53 -v $PWD/certbot/logs:/var/log/letsencrypt -v $PWD/certbot/letsencrypt:/etc/letsencrypt/ certbot-burp certonly -d $DOMAIN -d *.$DOMAIN --server https://acme-v02.api.letsencrypt.org/directory --agree-tos --no-eff-email --dns-cloudflare --register-unsafely-without-email --preferred-challenges dns-01 15 | -------------------------------------------------------------------------------- /certbot/renew.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -ne 1 ]; then 4 | echo usage: ./$0 \ 5 | exit 0 6 | fi 7 | 8 | DOMAIN=$1 9 | 10 | echo Running certbot for domain $DOMAIN 11 | 12 | docker run --rm --name certbot --hostname certbot -ti -p 53:53/udp -p 53:53 -v $PWD/certbot/logs:/var/log/letsencrypt -v $PWD/certbot/letsencrypt:/etc/letsencrypt/ certbot-burp renew --force-renewal 13 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Check if a file exists 6 | check_file() { 7 | if [ "$1" == "burp.jar" ]; then 8 | local file_path="./burp/pkg/$1" 9 | else 10 | local file_path="$1" 11 | fi 12 | 13 | if [ ! -e "$file_path" ]; then 14 | echo "ERROR: $file_path not found. Make sure it is in the correct location." 15 | exit 0 16 | fi 17 | } 18 | 19 | # Check if a command exists 20 | check_command() { 21 | which "$1" > /dev/null 2>&1 22 | if [ $? -eq 1 ]; then 23 | echo "ERROR: $1 is missing. Please install first." 24 | exit 0 25 | fi 26 | } 27 | 28 | handle_docker_permission_error() { 29 | echo "ERROR: Permission denied while trying to connect to the Docker daemon. Your user likely needs to use 'sudo' with docker, but we can add your user to the docker group and it should fix this." 30 | read -p "Would you like to add your user to the Docker group to fix this? (y/n): " choice 31 | if [ "$choice" == "y" ]; then 32 | sudo usermod -aG docker $USER 33 | echo "User added to the Docker group. Please log out and back in for the changes to take effect, then try the init script again." 34 | else 35 | echo "Exiting script. Please fix the Docker permissions manually." 36 | fi 37 | exit 1 38 | } 39 | 40 | if [ -e ./init.sh_has_been_run ]; then 41 | echo "Script has already been run. Bailing out." 42 | exit 0 43 | fi 44 | 45 | check_file "burp.jar" 46 | check_command "docker" 47 | check_command "bc" 48 | check_command "openssl" 49 | 50 | if [ $# -ne 2 ]; then 51 | echo "Usage: ./init.sh " 52 | exit 0 53 | fi 54 | 55 | DOMAIN=$1 56 | IP=$2 57 | METRICS=$(LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom | fold -w 10 | head -1) 58 | 59 | echo "Initialization to be done with domain *.$1 and public IP $2" 60 | read -p "Press any key to continue, or CTRL-C to bail out" var_p 61 | 62 | { 63 | # check if docker works 64 | docker container ls || handle_docker_permission_error 65 | 66 | # build the containers 67 | docker build -t certbot-burp certbot/certbot-dns-burp 68 | docker build -t burp burp 69 | 70 | # Get certs for the first time. The certbot container will be removed automatically afterwards. 71 | ./certbot/new.sh $DOMAIN 72 | 73 | # The symlinks from certbot will be wrong. 74 | # Copy the actual certificate files from the archive directory to burp/keys 75 | sudo cp ./certbot/letsencrypt/archive/$DOMAIN/cert1.pem ./burp/keys/cert.pem 76 | sudo cp ./certbot/letsencrypt/archive/$DOMAIN/chain1.pem ./burp/keys/chain.pem 77 | sudo cp ./certbot/letsencrypt/archive/$DOMAIN/fullchain1.pem ./burp/keys/fullchain.pem 78 | sudo cp ./certbot/letsencrypt/archive/$DOMAIN/privkey1.pem ./burp/keys/privkey.pem 79 | 80 | # Change ownership of the privkey.pem file to UID 999 and GID 999 81 | sudo chown 999:999 ./burp/keys/privkey.pem 82 | 83 | # Replace placeholders in burp config 84 | sudo /bin/sed -i "s/DOMAIN/$DOMAIN/g" ./burp/conf/burp.config 85 | sudo /bin/sed -i "s/IP/$IP/g" ./burp/conf/burp.config 86 | sudo /bin/sed -i "s/jnaicmez8/$METRICS/g" ./burp/conf/burp.config 87 | 88 | # run the burp container 89 | ./burp/run.sh 90 | sudo /bin/mv ./init.sh ./init.sh_has_been_run 91 | sudo /bin/chmod 000 ./init.sh_has_been_run 92 | 93 | # replace placeholders in renewal script 94 | sudo /bin/sed -i "s/__DOMAIN__/$DOMAIN/g" ./certbot/certificaterenewal.sh 95 | sudo /bin/sed -i "s#__BASEDIR__#$PWD#g" ./certbot/certificaterenewal.sh 96 | } || { 97 | echo "An error occurred during the execution of the script. Please check the output for details." 98 | exit 1 99 | } 100 | 101 | echo 102 | echo "SUCCESS! Burp is now running with the letsencrypt certificate for domain *.$DOMAIN" 103 | echo 104 | echo "Your metrics path was set to $METRICS. Change addressWhitelist to access it remotely." 105 | echo "Initialization script has completed." 106 | --------------------------------------------------------------------------------