-
-
Save jcubic/89f822213c837b48901dcb3128c75c3c to your computer and use it in GitHub Desktop.
Upload SSL Certificate to DirectAdmin controlled domains
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
output=$(mktemp); | |
sudo certbot certonly --manual --expand --manual-public-ip-logging-ok \ | |
--preferred-challenges http -n \ | |
-d <LIST OF COMMA SEPARATED DOMAINS AND SUBDOMAINS>\ | |
--manual-auth-hook ./cert.py --agree-tos --email <EMAIL ADRESS> 2>&1 | tee $output | |
grep "Certificate not yet due for renewal" $output > /dev/null || \ | |
sudo ./cert.py -d <COMMA SEPARATED LIST OF DOMAINS> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Script for lensencypt cert and challanges in DirectAdmin | |
# | |
# Copyright (c) 2018 Jakub Jankiewicz <https://jcubic.pl/me> | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
import urllib | |
import urllib2 | |
import httplib | |
import urlparse | |
import ssl | |
import re | |
import os | |
import tempfile | |
from ftplib import FTP, error_perm | |
import publicsuffix | |
username = '' | |
password = '' | |
host = '' | |
# url of direct admin page | |
directadmin = '' | |
def connection(url): | |
if url.scheme == 'http': | |
return httplib.HTTPConnection(url.netloc) | |
elif url.scheme == 'https': | |
return httplib.HTTPSConnection(url.netloc) | |
else: | |
raise Exception("wrong scheme %s" % (urls.scheme)) | |
def get(url, cookie = None, referer = None): | |
url = urlparse.urlparse(url) | |
conn = connection(url) | |
headers = {} | |
if cookie is not None: | |
headers['Cookie'] = cookie | |
if referer is not None: | |
headers['Referer'] = referer | |
conn.request("GET", url.path, headers = headers) | |
res = conn.getresponse() | |
headers = list2dict(res.getheaders()) | |
return res | |
def post(url, data, cookie = None, referer = None): | |
urlpart = urlparse.urlparse(url) | |
conn = connection(urlpart) | |
data = urllib.urlencode(data) | |
headers = {} | |
if cookie is not None: | |
headers['Cookie'] = cookie | |
if referer is not None: | |
headers['Referer'] = referer | |
conn.request("POST", urlpart.path, data, headers) | |
res = conn.getresponse() | |
headers = list2dict(res.getheaders()) | |
if headers.has_key('set-cookie'): | |
cookies = headers['set-cookie'].split(';')[0].strip() | |
if headers.has_key('location'): | |
return get(headers['location'], cookies, referer = url) | |
return res | |
def list2dict(lst): | |
dict = {} | |
for k,v in lst: | |
dict[k] = v | |
return dict | |
ssl._https_verify_certificates(False) | |
def head(url): | |
url = urlparse.urlparse(url) | |
conn = connection(url) | |
if url.path == '': | |
conn.request("HEAD", '/') | |
else: | |
conn.request("HEAD", url.path) | |
return conn.getresponse() | |
def location(url): | |
while True: | |
res = head(url) | |
headers = list2dict(res.getheaders()) | |
if not headers.has_key('location'): | |
return url | |
url = headers['location'] | |
def placeFiles(ftp, path): | |
for name in os.listdir(path): | |
localpath = os.path.join(path, name) | |
if os.path.isfile(localpath): | |
print "writing %s" % name | |
ftp.storbinary('STOR ' + name, open(localpath,'rb')) | |
elif os.path.isdir(localpath): | |
if name not in ftp.nlst(): | |
print "mkdir %s" % name | |
ftp.mkd(name) | |
else: | |
print "process %s" % name | |
ftp.cwd(name) | |
placeFiles(ftp, localpath) | |
ftp.cwd("..") | |
def mkchallange(host, username, password, directory = '/', quiet = False): | |
tmp_directory = tempfile.mkdtemp() | |
path = os.path.join(tmp_directory, '.well-known', 'acme-challenge') | |
os.makedirs(path) | |
fname = os.environ['CERTBOT_TOKEN'] | |
f = open(os.path.join(path, fname), 'w') | |
f.write(os.environ['CERTBOT_VALIDATION']) | |
f.close() | |
con = FTP(host) | |
con.login(username, password) | |
con.cwd(directory) | |
placeFiles(con, tmp_directory) | |
if __name__ == '__main__': | |
from optparse import OptionParser | |
from sys import argv | |
parser = OptionParser() | |
parser.add_option('-c', '--cert') | |
parser.add_option('-d', '--domains') | |
(options, args) = parser.parse_args() | |
if options.domains is None: | |
if os.environ.has_key('CERTBOT_DOMAIN'): | |
psl_file = publicsuffix.fetch() | |
psl = publicsuffix.PublicSuffixList(psl_file) | |
domain = psl.get_public_suffix(os.environ['CERTBOT_DOMAIN']) | |
print "upload %s on %s domain" % (os.environ['CERTBOT_DOMAIN'], domain) | |
sub = os.environ['CERTBOT_DOMAIN'].replace(domain, '') | |
if len(sub) > 0 and sub[0] == '.': | |
sub = sub[1:] | |
path = '/domains/%s/public_html/%s' % (domain, sub) | |
mkchallange(host, username, password, path) | |
else: | |
print "\n".join(["usage: %s OPTIONS" % (argv[0]), | |
"-c --cert first domain from certbot", | |
"-d --d comma separated domains names"]) | |
else: | |
domains = [x.strip() for x in options.domains.split(',')] | |
if options.cert is None: | |
cert_dir = domains[0] | |
else: | |
cert_dir = options.cert | |
full = open('/etc/letsencrypt/live/%s/fullchain.pem' % (cert_dir)) | |
private = open('/etc/letsencrypt/live/%s/privkey.pem' % (cert_dir)) | |
cert = "%s\n%s" % (full.read(), private.read()) | |
ca = get('https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt').read() | |
full.close() | |
private.close() | |
url = location(directadmin) | |
res = post("".join([url, '/CMD_LOGIN']), { | |
'referer': '/CMD_LOGIN', | |
'username': username, | |
'password': password | |
}) | |
headers = list2dict(res.getheaders()) | |
cookies = headers['set-cookie'].split(';')[0].strip() | |
for domain in domains: | |
domain = domain = psl.get_public_suffix(domain) | |
data = { | |
'domain':domain, | |
'action':'save', | |
'certificate': cert, | |
'type': 'paste', | |
'submit': 'Zapisz' | |
} | |
referer = "".join([url, "/CMD_SSL?domain=%s" % domain]) | |
html = post("".join([url, '/CMD_SSL']), data, cookies, referer = referer).read() | |
if re.search("Certyfikat i Klucz", html): | |
print "Successful Certificate upload" | |
else: | |
print "Error Certificate upload" | |
data = { | |
'domain': domain, | |
'action': 'save', | |
'active': 'yes', | |
'type': 'cacert', | |
'cacert': ca, | |
'submit': 'Zapisz' | |
} | |
referer = "".join([url, '/CMD_SSL?DOMAIN=%s&view=cacert' % domain]) | |
html = post("".join([url, '/CMD_SSL']), data, cookies, referer = referer).read() | |
if re.search("Powodzenie", html): | |
print "Successful Root Certificate upload" | |
else: | |
print "Error Root Certificate upload" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You need to update this script with:
re.search
invocation) and value ofsubmit
in post data/domains/<DOMAIN>/public_html/<SUB DOMAIN>
The script require publicsuffix2 module
And of course certbot tool to get certificate from letsencrypt, to install on GNU/Linux
or