From 64034d4e269ad42ed8e76de02a6fd67571be1044 Mon Sep 17 00:00:00 2001 From: Jason Heard Date: Sun, 15 Nov 2015 15:34:26 -0700 Subject: Add file-based challenge hosting --- sign_csr.py | 57 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/sign_csr.py b/sign_csr.py index 815c4ea..ff9332b 100644 --- a/sign_csr.py +++ b/sign_csr.py @@ -2,7 +2,43 @@ import argparse, subprocess, json, os, urllib2, sys, base64, binascii, time, \ hashlib, tempfile, re, copy, textwrap -def sign_csr(pubkey, csr, email=None): + +def host_token(domain, token, response_payload, user_step_number, file_based): + if file_based: + response_url = 'http://{}.well-known/acme-challenge/{}'.format(domain, token) + + # tell the user where to put a file + sys.stderr.write("""\ +STEP {}: You need to place a file on your webserver so that the URL {} will +resolve to a file with the contents: + +-------------- +{} +-------------- + +""".format(user_step_number, response_url, response_payload)) + else: + sys.stderr.write("""\ +STEP {}: You need to run this command on {} (don't stop the python command until the next step). + +openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes +sudo python -c "import BaseHTTPServer; \\ + import ssl; \\ + h = BaseHTTPServer.BaseHTTPRequestHandler; \\ + h.do_GET = lambda r: r.send_response(200) or r.end_headers() or r.wfile.write('{}'); \\ + s = BaseHTTPServer.HTTPServer(('0.0.0.0', 443), h); \\ + s.socket = ssl.wrap_socket(s.socket, certfile='server.pem', server_side=True); \\ + s.serve_forever()" + +""".format(user_step_number, domain, response_payload.replace('"', '\\"'))) + + stdout = sys.stdout + sys.stdout = sys.stderr + raw_input("Press Enter when you've got the python command running on your server...") + sys.stdout = stdout + + +def sign_csr(pubkey, csr, email=None, file_based=False): """Use the ACME protocol to get an ssl certificate signed by a certificate authority. @@ -287,21 +323,7 @@ STEP 3: You need to sign some more files (replace 'user.key' with your user priv # Step 11: Ask the user to host the token on their server for n, i in enumerate(ids): - sys.stderr.write("""\ -STEP {}: You need to run this command on {} (don't stop the python command until the next step). - -sudo python -c "import BaseHTTPServer; \\ - h = BaseHTTPServer.BaseHTTPRequestHandler; \\ - h.do_GET = lambda r: r.send_response(200) or r.end_headers() or r.wfile.write('{}'); \\ - s = BaseHTTPServer.HTTPServer(('0.0.0.0', 80), h); \\ - s.serve_forever()" - -""".format(n+4, i['domain'], responses[n]['data'])) - - stdout = sys.stdout - sys.stdout = sys.stderr - raw_input("Press Enter when you've got the python command running on your server...") - sys.stdout = stdout + host_token(i['domain'], challenge['token'], responses[n]['data'], n + 4, file_based) # Step 12: Let the CA know you're ready for the challenge sys.stderr.write("Requesting verification for {}...\n".format(i['domain'])) @@ -409,9 +431,10 @@ $ python sign_csr.py --public-key user.pub domain.csr > signed.crt """) parser.add_argument("-p", "--public-key", required=True, help="path to your account public key") parser.add_argument("-e", "--email", default=None, help="contact email, default is webmaster@") + parser.add_argument("-f", "--file-based", action='store_true', help="if set, a file-based response is used") parser.add_argument("csr_path", help="path to your certificate signing request") args = parser.parse_args() - signed_crt = sign_csr(args.public_key, args.csr_path, email=args.email) + signed_crt = sign_csr(args.public_key, args.csr_path, email=args.email, file_based=args.file_based) sys.stdout.write(signed_crt) -- cgit v1.2.3 From f1e2df903b1c03a9c7ea1e90edd0a11c8df300c3 Mon Sep 17 00:00:00 2001 From: Jason Heard Date: Sun, 15 Nov 2015 15:35:35 -0700 Subject: Update README with file-based option --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a16d5fb..18d5b26 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ optional arguments: path to your account public key -e EMAIL, --email EMAIL contact email, default is webmaster@ + -f, --file-based if set, a file-based response is used user@hostname:~$ ``` -- cgit v1.2.3 From a45000064c07c418d33bec36945c1dea8bdada26 Mon Sep 17 00:00:00 2001 From: Jason Heard Date: Sun, 15 Nov 2015 16:07:10 -0700 Subject: Add missing slash to URL --- sign_csr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sign_csr.py b/sign_csr.py index ff9332b..a6987ff 100644 --- a/sign_csr.py +++ b/sign_csr.py @@ -5,7 +5,7 @@ import argparse, subprocess, json, os, urllib2, sys, base64, binascii, time, \ def host_token(domain, token, response_payload, user_step_number, file_based): if file_based: - response_url = 'http://{}.well-known/acme-challenge/{}'.format(domain, token) + response_url = 'http://{}/.well-known/acme-challenge/{}'.format(domain, token) # tell the user where to put a file sys.stderr.write("""\ -- cgit v1.2.3 From 6c2a1195a8e4e43de20586baa355c88a3350c0d9 Mon Sep 17 00:00:00 2001 From: Jason Heard Date: Tue, 17 Nov 2015 10:42:48 -0700 Subject: Address review comments --- sign_csr.py | 75 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/sign_csr.py b/sign_csr.py index a6987ff..f6fe24c 100644 --- a/sign_csr.py +++ b/sign_csr.py @@ -3,41 +3,6 @@ import argparse, subprocess, json, os, urllib2, sys, base64, binascii, time, \ hashlib, tempfile, re, copy, textwrap -def host_token(domain, token, response_payload, user_step_number, file_based): - if file_based: - response_url = 'http://{}/.well-known/acme-challenge/{}'.format(domain, token) - - # tell the user where to put a file - sys.stderr.write("""\ -STEP {}: You need to place a file on your webserver so that the URL {} will -resolve to a file with the contents: - --------------- -{} --------------- - -""".format(user_step_number, response_url, response_payload)) - else: - sys.stderr.write("""\ -STEP {}: You need to run this command on {} (don't stop the python command until the next step). - -openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes -sudo python -c "import BaseHTTPServer; \\ - import ssl; \\ - h = BaseHTTPServer.BaseHTTPRequestHandler; \\ - h.do_GET = lambda r: r.send_response(200) or r.end_headers() or r.wfile.write('{}'); \\ - s = BaseHTTPServer.HTTPServer(('0.0.0.0', 443), h); \\ - s.socket = ssl.wrap_socket(s.socket, certfile='server.pem', server_side=True); \\ - s.serve_forever()" - -""".format(user_step_number, domain, response_payload.replace('"', '\\"'))) - - stdout = sys.stdout - sys.stdout = sys.stderr - raw_input("Press Enter when you've got the python command running on your server...") - sys.stdout = stdout - - def sign_csr(pubkey, csr, email=None, file_based=False): """Use the ACME protocol to get an ssl certificate signed by a certificate authority. @@ -46,6 +11,10 @@ def sign_csr(pubkey, csr, email=None, file_based=False): :param string csr: Path to the certificate signing request. :param string email: An optional user account contact email (defaults to webmaster@) + :param bool file_based: An optional flag indicating that the + hosting should be file-based rather + than providing a simple python HTTP + server. :returns: Signed Certificate (PEM format) :rtype: string @@ -323,7 +292,41 @@ STEP 3: You need to sign some more files (replace 'user.key' with your user priv # Step 11: Ask the user to host the token on their server for n, i in enumerate(ids): - host_token(i['domain'], challenge['token'], responses[n]['data'], n + 4, file_based) + if file_based: + sys.stderr.write("""\ +STEP {}: Please update your server to serve the following file at this URL: + +-------------- +URL: http://{}/.well-known/acme-challenge/{} +File contents: \"{}\" +-------------- + +Notes: +- Do not include the quotes in the file. +- The file should be one line without any spaces. + +""".format(n + 4, i['domain'], challenge['token'], responses[n]['data'])) + + stdout = sys.stdout + sys.stdout = sys.stderr + raw_input("Press Enter when you've got the file hosted on your server...") + sys.stdout = stdout + else: + sys.stderr.write("""\ +STEP {}: You need to run this command on {} (don't stop the python command until the next step). + +sudo python -c "import BaseHTTPServer; \\ + h = BaseHTTPServer.BaseHTTPRequestHandler; \\ + h.do_GET = lambda r: r.send_response(200) or r.end_headers() or r.wfile.write('{}'); \\ + s = BaseHTTPServer.HTTPServer(('0.0.0.0', 80), h); \\ + s.serve_forever()" + +""".format(n + 4, i['domain'], responses[n]['data'])) + + stdout = sys.stdout + sys.stdout = sys.stderr + raw_input("Press Enter when you've got the python command running on your server...") + sys.stdout = stdout # Step 12: Let the CA know you're ready for the challenge sys.stderr.write("Requesting verification for {}...\n".format(i['domain'])) -- cgit v1.2.3