diff --git a/.gitignore b/.gitignore index e69de29..352cd74 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +/acme-tiny-5a7b4e7.tar.gz diff --git a/README-fedora.md b/README-fedora.md new file mode 100644 index 0000000..ecfc538 --- /dev/null +++ b/README-fedora.md @@ -0,0 +1,133 @@ +# acme-tiny Fedora package + +The Fedora package for acme-tiny adds a tiny framework to make issuing +and renewing [Let's Encrypt](https://letsencrypt.org/) certificates +convenient. It does *not* alter your configuration in any way, other +than to drop an acme.conf apache config snippet into `/etc/httpd/conf.d` +and provide a systemd service. + +If you want a package that tries to do everything for you as root, +consider the `certbot` package. + +The ACME protocol will work with other certificate authorities, but +acme-tiny is currently hardwired to use letencrypt.org - which is +also currently the only ACME certificate authority recognized in +most browsers. + +These instructions assume you are using letsencrypt for the first time +with this acme-tiny package. For example, you should not already have +an account key for the domains it will manage. If you do, see README.md +for instructions on converting it. Put any existing account key in +PEM format in `/var/lib/acme/private`, readable by the acme user (only!). + +## The web server must already serve your domains on HTTP + +If you cannot access your web domains locally with commands like +`curl` and `wget`, then this framework won't work. Acme-tiny will work with +any web server package, but if you are not using apache (httpd package), you +must provide the equivalent of `/etc/httpd/conf.d/acme.conf` to map +`/var/www/challenges` to the ACME URL location. The web server can even +be on a remote machine - provided you have somehow arranged for it to +serve files from `/var/www/challenges` (perhaps via NFS). + +If you are using Apache, and restrict access to ``, then this +will override the acme.conf global config snippet, and you must explicitly +make the ACME URL (http://your.domain.here/.well-known/acme-challenge/) +publicly accessible. + +## Put your CSRs in `/var/lib/acme/csr` + +You can use existing CSRs, or generate a new one using openssl. Put +all CSRs to be issued and renewed by acme-tiny in `/var/lib/acme/csr`. +I like to symlink the CSRs into /var/lib/acme/csr, just make sure the acme +user can read them (and follow the symlink). The details for openssl are +beyond the scope of this documentation, but this should work for creating a +certificate for a single domain: +``` +cd /etc/pki/tls +ln -s /var/lib/acme/csr . +openssl req -new -nodes -keyout private/your.domain.key \ + -out csr/your.domain.csr +chmod 0400 private/your.domain.key +``` +If you have an existing key, replace `-nodes -keyout` with `-key`. +The default openssl config will ask you for data, be sure to give the +domain you will be serving when it asks for "Common Name". It is possible +to cover multiple domains with a single certificate using openssl. First, add +a section to the end of `/etc/pki/tls/openssl.cnf` defining your extension: +``` +[MYSERV] +subjectAltName=DNS:your.domain,DNS:www.your.domain +``` +Then add `-reqexts MYSERV` to the openssl command line. One of the domains +must match the common name. + +Make sure the CSR can be read by the acme user. + +## Issue the certificate + +The timer service in acme-tiny will check the certificate for all CSRs in csr +every 24 hours, and issue or renew the certificate if it is missing or about to +expire (in 7 days by default). You don't have to wait for the timer, however. +Use +``` +systemctl start acme-tiny +``` +to run the service now. The certificate should appear in `/var/lib/acme/certs`, +and errors will be in journalctl. Alternatively (and on EL6), run +`/usr/libexec/acme-tiny/sign` as the acme user, and errors will go +to your terminal. + +## Use the certificate + +The default httpd config uses a self-signed localhost certificate for https. +Edit `/etc/httpd/conf.d/ssl.conf` and change `SSLCertificateFile` and +`SSLCertificateChainFile` to `/var/lib/acme/certs/your.domain.crt` (or use a +symlink to /etc/pki/tls/certs). Change `SSLCertificateKeyFile` to +`/etc/pki/tls/private/your.domain.key`. + +Obviously, you can change the locations to suit your sysadmin tastes. + +Some SSL apps, like dovecot, require SSL certificates to be tagged in selinux. +``` +semanage fcontext -a -f 'all files' -t cert_t '/var/lib/acme/certs(/.*)?' +restorecon -rv /var/lib/acme/certs +``` +The above will permanently change the selinux tag to work with dovecot +and other apps. + +Sendmail is a special problem - it insists that any certificates it loads be +only writable by root. This is at odds with the privilege separation of the +acme user. (Obviously, the private key must be accessible only by root.) You +can, of course, copy the crt file to /etc/pki/tls/certs as root and change the +mode. But this has to be done every time the cert is renewed. You can +install `incron` to do this. After installing, create `/etc/incron.d/acme` +with the line +``` +/var/lib/acme/certs/mail.crt IN_MOVED_TO cp $@ /etc/pki/tls/certs +``` +where `mail.crt` is the certificate sendmail will use. Sendmail +can then load it from /etc/pki/tls/certs and be happy. This also +solves the file context problem if you add lines for other certificates. +You might wonder why we don't simply supply an acme incrontab as part +of the package with a wildcard, for example: +``` +/var/lib/acme/certs/*.crt IN_MOVED_TO cp $@ /etc/pki/tls/certs +``` +The answer is that incron is insecure, and very nasty things can be +done by putting shell meta characters (including semicolon and quote!) in +filenames that then become part of a command run as root. The first example +above uses a fixed filename, so that is safe. Complain to incron +upstream - they need an option to use a simple execvpe instead of +using the shell. Then it would at least be possible to carefully +handle [malicious names](https://www.xkcd.com/327/). + +## Virtual Hosts + +Most web servers can handle multiple logical web hosts - configuring that is +beyond the scope of this document. Each virtual host may need to have its own +certificate for SSL. They can all share the same key file (see above for +how to use an existing key for certificate requests), or use different keys. +Put all the CSRs in /var/lib/acme/csr and the acme-tiny service will keep them +all renewed. This also works for certificates used by other SSL applications, +such as dovecot, sendmail, jabberd, or znc. diff --git a/acme-tiny-chain.patch b/acme-tiny-chain.patch new file mode 100644 index 0000000..17e46b9 --- /dev/null +++ b/acme-tiny-chain.patch @@ -0,0 +1,115 @@ +diff -up ./acme_tiny.py.chain ./acme_tiny.py +--- ./acme_tiny.py.chain 2016-08-01 15:53:22.000000000 -0400 ++++ ./acme_tiny.py 2016-08-22 19:20:51.208523709 -0400 +@@ -1,4 +1,4 @@ +-#!/usr/bin/env python ++#!/usr/bin/python + import argparse, subprocess, json, os, sys, base64, binascii, time, hashlib, re, copy, textwrap, logging + try: + from urllib.request import urlopen # Python 3 +@@ -12,7 +12,7 @@ LOGGER = logging.getLogger(__name__) + LOGGER.addHandler(logging.StreamHandler()) + LOGGER.setLevel(logging.INFO) + +-def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA): ++def get_crt(account_key, csr, acme_dir, log=LOGGER, CA=DEFAULT_CA, chain=False): + # helper function base64 encode for jose spec + def _b64(b): + return base64.urlsafe_b64encode(b).decode('utf8').replace("=", "") +@@ -57,9 +57,9 @@ def get_crt(account_key, csr, acme_dir, + }) + try: + resp = urlopen(url, data.encode('utf8')) +- return resp.getcode(), resp.read() ++ return resp.getcode(), resp.read(), resp.info() + except IOError as e: +- return getattr(e, "code", None), getattr(e, "read", e.__str__)() ++ return getattr(e, "code", None), getattr(e, "read", e.__str__)(), None + + # find domains + log.info("Parsing CSR...") +@@ -80,7 +80,7 @@ def get_crt(account_key, csr, acme_dir, + + # get the certificate domains and expiration + log.info("Registering account...") +- code, result = _send_signed_request(CA + "/acme/new-reg", { ++ code, result, headers = _send_signed_request(CA + "/acme/new-reg", { + "resource": "new-reg", + "agreement": "https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf", + }) +@@ -96,7 +96,7 @@ def get_crt(account_key, csr, acme_dir, + log.info("Verifying {0}...".format(domain)) + + # get new challenge +- code, result = _send_signed_request(CA + "/acme/new-authz", { ++ code, result, headers = _send_signed_request(CA + "/acme/new-authz", { + "resource": "new-authz", + "identifier": {"type": "dns", "value": domain}, + }) +@@ -123,7 +123,7 @@ def get_crt(account_key, csr, acme_dir, + wellknown_path, wellknown_url)) + + # notify challenge are met +- code, result = _send_signed_request(challenge['uri'], { ++ code, result, headers = _send_signed_request(challenge['uri'], { + "resource": "challenge", + "keyAuthorization": keyauthorization, + }) +@@ -153,17 +153,32 @@ def get_crt(account_key, csr, acme_dir, + proc = subprocess.Popen(["openssl", "req", "-in", csr, "-outform", "DER"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + csr_der, err = proc.communicate() +- code, result = _send_signed_request(CA + "/acme/new-cert", { ++ code, result, headers = _send_signed_request(CA + "/acme/new-cert", { + "resource": "new-cert", + "csr": _b64(csr_der), + }) + if code != 201: + raise ValueError("Error signing certificate: {0} {1}".format(code, result)) + ++ certchain = [result] ++ if chain: ++ def parse_link_header(line): ++ m = re.search(r"^Link:\s*<([^>]*)>(?:\s*;\s*(.*))?\r\n$", line) ++ return (m.group(1), dict([(a[0],a[1].strip('"')) ++ for a in [attr.split("=") ++ for attr in m.group(2).split("\s*;\s*")]])) ++ ++ up = [ ++ link for link, attr in [ ++ parse_link_header(l) for l in headers.getallmatchingheaders("Link") ++ ] if attr['rel'] == 'up' ++ ] ++ certchain += [urlopen(url).read() for url in up] ++ + # return signed certificate! + log.info("Certificate signed!") +- return """-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format( +- "\n".join(textwrap.wrap(base64.b64encode(result).decode('utf8'), 64))) ++ return "".join(["""-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n""".format( ++ "\n".join(textwrap.wrap(base64.b64encode(cert).decode('utf8'), 64))) for cert in certchain]) + + def main(argv): + parser = argparse.ArgumentParser( +@@ -188,11 +203,19 @@ def main(argv): + parser.add_argument("--acme-dir", required=True, help="path to the .well-known/acme-challenge/ directory") + parser.add_argument("--quiet", action="store_const", const=logging.ERROR, help="suppress output except for errors") + parser.add_argument("--ca", default=DEFAULT_CA, help="certificate authority, default is Let's Encrypt") ++ parser.add_argument("--chain", action="store_true", ++ help="fetch and append intermediate certs to output") + + args = parser.parse_args(argv) + LOGGER.setLevel(args.quiet or LOGGER.level) +- signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, log=LOGGER, CA=args.ca) +- sys.stdout.write(signed_crt) ++ try: ++ signed_crt = get_crt(args.account_key, args.csr, args.acme_dir, ++ log=LOGGER, CA=args.ca, chain=args.chain) ++ sys.stdout.write(signed_crt) ++ except Exception as e: ++ #if not args.quiet: raise e ++ LOGGER.error(e) ++ sys.exit(1) + + if __name__ == "__main__": # pragma: no cover + main(sys.argv[1:]) diff --git a/acme-tiny-sign.sh b/acme-tiny-sign.sh new file mode 100644 index 0000000..c3005b3 --- /dev/null +++ b/acme-tiny-sign.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +if test "$(id -u)" -eq 0; then + echo "Do not run as root!" + exit 2 +fi + +DAYS="${1:-7}" + +cd /var/lib/acme + +if ! test -s private/account.key; then + touch private/account.key + chmod 0600 private/account.key + openssl genrsa 4096 >private/account.key +fi + +for csr in csr/*.csr; do + test -s "$csr" || continue + test -r "$csr" || continue + crt="${csr%%.csr}" + tmp="certs/${crt##csr/}.tmp" + crt="certs/${crt##csr/}.crt" + if test -s "$crt" && /usr/sbin/cert-check --days="$DAYS" "$crt"; then + continue + fi + test -w "$crt" || test ! -e "$crt" || continue + echo acme_tiny --account-key private/account.key --csr "$csr" \ + --acme-dir /var/www/challenges/ --chain --out "$crt" + + if /usr/sbin/acme_tiny --account-key private/account.key --csr "$csr" \ + --acme-dir /var/www/challenges/ --chain > "$tmp"; then + mv "$tmp" "$crt" + else + test -e "$tmp" && test ! -s "$tmp" && rm "$tmp" + fi + # append intermediate certs + #cat *.pem >>"$crt" +done diff --git a/acme-tiny.cron b/acme-tiny.cron new file mode 100644 index 0000000..206fd39 --- /dev/null +++ b/acme-tiny.cron @@ -0,0 +1,3 @@ +# Check daily for csrs in /var/lib/acme/csr that need signing or renewing +# within 7 days. +31 2 * * * acme /usr/libexec/acme-tiny/sign 7 diff --git a/acme-tiny.service b/acme-tiny.service new file mode 100644 index 0000000..a9bedfc --- /dev/null +++ b/acme-tiny.service @@ -0,0 +1,15 @@ +[Unit] +Description=Check for acme certs about to expire + +[Service] +Type=oneshot +Nice=19 +ProtectHome=true +ProtectSystem=true +User=acme +Group=acme +SyslogIdentifier=acme-tiny +ExecStart=/usr/libexec/acme-tiny/sign 7 + +[Install] +Also=acme-tiny.timer diff --git a/acme-tiny.spec b/acme-tiny.spec new file mode 100644 index 0000000..12054ee --- /dev/null +++ b/acme-tiny.spec @@ -0,0 +1,181 @@ +%global commit 5a7b4e79bc9bd5b51739c0d8aaf644f62cc440e6 +%global shortcommit 5a7b4e7 + +%if 0%{?rhel} >= 5 && 0%{?rhel} < 7 +%global use_systemd 0 +%else +%global use_systemd 1 +%endif + +%if 0%{?fedora} +# Explicity require python3 on Fedora to help track which packages +# no longer need python2. +%global use_python3 1 +%else +%global use_python3 0 +%endif + +Name: acme-tiny +Version: 0.1 +Release: 10.git%{shortcommit}%{?dist} +Summary: Tiny auditable script to issue, renew Let's Encrypt certificates + +Group: Applications/Internet +License: MIT +URL: https://github.com/diafygi/acme-tiny +Source0: https://github.com/diafygi/%{name}/archive/%{commit}.tar.gz#/%{name}-%{shortcommit}.tar.gz +Source1: acme-tiny-sign.sh +Source2: cert-check.py +Source3: acme.conf +Source4: lets-encrypt-x3-cross-signed.pem +Source5: acme-tiny.cron +Source6: acme-tiny.timer +Source7: acme-tiny.service +Source8: README-fedora.md +# Fetch and include intermediate cert(s), too. +Patch0: acme-tiny-chain.patch + +Requires: openssl +Requires(pre): shadow-utils +%if %{use_systemd} +# systemd macros are not defined unless systemd is present +BuildRequires: systemd +%{?systemd_requires} +%else +Requires: cronie +%endif +BuildArch: noarch +%if 0%{?fedora} > 22 +Suggests: httpd, mod_ssl, nginx +Enhances: httpd, mod_ssl, nginx +%endif + +%description +This is a tiny, auditable script that you can throw on your server to issue and +renew Let's Encrypt certificates. Since it has to be run on your server and +have access to your private Let's Encrypt account key, I tried to make it as +tiny as possible (currently less than 200 lines). The only prerequisites are +python and openssl. + +Well, that and a web server - but then you only need this with a web server. +This package adds a simple directory layout and timer service that runs +acme_tiny on installed CSRs as the acme user for privilege separation. + +%prep +%setup -q -n %{name}-%{commit} +%patch0 -p1 -b .chain +cp -p %{SOURCE1} %{SOURCE2} %{SOURCE8} . +%if %{use_python3} +sed -i.old -e '1,1 s/python$/python3/' *.py +%endif + +%build + +%install +mkdir -p %{buildroot}/var/www/challenges +mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d +mkdir -p %{buildroot}%{_sbindir} +mkdir -p %{buildroot}%{_libexecdir}/%{name} +mkdir -p %{buildroot}%{_sharedstatedir}/acme/{private,csr,certs} +chmod 0700 %{buildroot}%{_sharedstatedir}/acme/private + +install -m 0755 acme-tiny-sign.sh %{buildroot}%{_libexecdir}/%{name}/sign +install -m 0755 acme_tiny.py %{buildroot}%{_sbindir}/acme_tiny +ln -sf acme_tiny %{buildroot}%{_sbindir}/acme-tiny +ln -sf %{_libexecdir}/%{name}/sign %{buildroot}%{_sbindir}/acme-tiny-sign +install -m 0755 cert-check.py %{buildroot}%{_sbindir}/cert-check +install -m 0644 %{SOURCE3} %{buildroot}%{_sysconfdir}/httpd/conf.d +install -m 0644 %{SOURCE4} %{buildroot}%{_sharedstatedir}/acme +%if %{use_systemd} +mkdir -p %{buildroot}%{_unitdir} +install -pm 644 %{SOURCE6} %{buildroot}%{_unitdir} +install -pm 644 %{SOURCE7} %{buildroot}%{_unitdir} +%else +mkdir -p %{buildroot}%{_sysconfdir}/cron.d +install -m 0644 %{SOURCE5} %{buildroot}%{_sysconfdir}/cron.d/acme-tiny +%endif + +%pre +getent group acme > /dev/null || groupadd -r acme +getent passwd acme > /dev/null || /usr/sbin/useradd -g acme \ + -c "Tiny Auditable ACME Client" \ + -r -d %{_sharedstatedir}/acme -s /sbin/nologin acme +exit 0 + +%if %{use_systemd} + +%post +%systemd_post acme-tiny.service acme-tiny.timer + +%postun +%systemd_postun_with_restart acme-tiny.service acme-tiny.timer + +%preun +%systemd_preun acme-tiny.service acme-tiny.timer + +%endif + +%files +%{!?_licensedir:%global license %%doc} +%license LICENSE +%doc README.md README-fedora.md +%attr(0755,acme,acme) /var/www/challenges +%attr(-,acme,acme) %{_sharedstatedir}/acme +%{_libexecdir}/%{name} +%config(noreplace) %{_sysconfdir}/httpd/conf.d/acme.conf +%if %{use_systemd} +%{_unitdir}/* +%else +%config(noreplace) %{_sysconfdir}/cron.d/acme-tiny +%endif +/usr/sbin/acme_tiny +/usr/sbin/acme-tiny +/usr/sbin/acme-tiny-sign +/usr/sbin/cert-check + +%changelog +* Mon Aug 22 2016 Stuart D. Gathman 0.1-10 +- Fix cert writable check in sign script +- More tips in README-fedora.md + +* Mon Aug 22 2016 Stuart D. Gathman 0.1-9 +- Use %%{systemd_requires} +- Remove unneeded cronie, python dependencies +- Add acme-tiny.timer to systemd scriptlets +- Add README-fedora.md +- acme_tiny: Fix --chain patch for python2.6 (el6) +- acme_tiny: Suppress traceback on error + +* Sun Aug 21 2016 Stuart D. Gathman 0.1-8 +- Add use_systemd flag to use systemd timer and enable on Fedora and epel7 +- Enable use_python3 flag for Fedora (but not epel7). + +* Sat Aug 20 2016 Stuart D. Gathman 0.1-7 +- sign: Actually use the new --chain flag +- cron: Make days to expiration explicit +- spec: Set file modes with install +- acme.conf: mark as config + +* Fri Aug 19 2016 Stuart D. Gathman 0.1-6 +- Python3 fixes for cert-check +- acme-tiny: Update patch to leave default behavior unchanged +- make /var/lib/acme readable by all except private + +* Thu Aug 11 2016 Stuart D. Gathman 0.1-5 +- sign: Use tmp output to avoid wiping existing cert on error +- acme_tiny: get intermediate cert from acme protocol + +* Thu Aug 11 2016 Stuart D. Gathman 0.1-4 +- Fix path of acme_tiny and make days explicit in sign script +- Add crontab + +* Wed Aug 10 2016 Stuart D. Gathman 0.1-3 +- Add global acme httpd conf +- Append intermediate certs, add current lets-encrypt intermediate cert + +* Tue Aug 9 2016 Stuart D. Gathman 0.1-2 +- add private, csr, certs directories +- add sign script suitable for cron + +* Mon Aug 8 2016 Stuart D. Gathman 0.1-1 +- Initial RPM diff --git a/acme-tiny.timer b/acme-tiny.timer new file mode 100644 index 0000000..687c820 --- /dev/null +++ b/acme-tiny.timer @@ -0,0 +1,13 @@ +[Unit] +Description=check for acme certs about to expire and renew them +ConditionKernelCommandLine=!rd.live.image +After=network-online.target +After=httpd.service nginx.service + +[Timer] +OnBootSec=20min +OnUnitInactiveSec=24h +Unit=acme-tiny.service + +[Install] +WantedBy=timers.target diff --git a/acme.conf b/acme.conf new file mode 100644 index 0000000..3768c5c --- /dev/null +++ b/acme.conf @@ -0,0 +1,17 @@ +Alias /.well-known/acme-challenge/ "/var/www/challenges/" + +# Note, blocking access to in a will override +# these global permissions. You will need to modify those domains +# to allow access to /.well-known/, or just copy the from below. +# See: http://httpd.apache.org/docs/2.2/sections.html + + + Options -Indexes + Order allow,deny + Allow from all + + + Options -Indexes + Order allow,deny + Allow from all + diff --git a/cert-check.py b/cert-check.py new file mode 100644 index 0000000..89f545c --- /dev/null +++ b/cert-check.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +from __future__ import print_function +from sys import stderr + +import subprocess, time, calendar, os, getopt + +def usage(): + print("""Usage: cert-check [options] files ... + -h,--help this message + -q,--quiet do not print cert files needing (re)newing + -d n,--days=n days before expiration to renew (default 7) +Succeeds only if all certs exist and are more than from expiration.""", + file=stderr) + return 2 + +def main(argv): + days = 7 # days ahead to + quiet = False + + try: + opts,args = getopt.getopt(argv,'hqd:',['days=','quiet','help']) + except getopt.GetoptError as err: + # print help information and exit: + print(err,file=stderr) # prints something like "option -a not recognized" + return usage() + + for opt,val in opts: + if opt in ('-h','--help'): + return usage() + if opt in ('-q','--quiet'): + quiet = True + if opt in ('-d','--days'): + try: + days = int(val) + except: + return usage() + + now = time.time() + soon = now + days * 24 * 60 * 60 + rc = 0 + + for fn in args: + try: + size = os.path.getsize(fn) + except: + size = 0 + if size == 0: + if not quiet: print(fn) + rc += 1 + continue + proc = subprocess.Popen( + ["openssl", "x509", "-in", fn, "-noout", "-enddate"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = proc.communicate() + if proc.returncode != 0: + raise IOError("{1}: OpenSSL Error: {0}".format(err,fn)) + t = time.strptime(out.decode(),'notAfter=%b %d %H:%M:%S %Y GMT\n') + t = calendar.timegm(t) + if soon > t: + if not quiet: print(fn) + rc += 1 + return rc > 0 + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv[1:])) diff --git a/lets-encrypt-x3-cross-signed.pem b/lets-encrypt-x3-cross-signed.pem new file mode 100644 index 0000000..0002462 --- /dev/null +++ b/lets-encrypt-x3-cross-signed.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- diff --git a/sources b/sources index e69de29..6e92207 100644 --- a/sources +++ b/sources @@ -0,0 +1 @@ +c18d4e5cceae3a01aae4fb2d1aa93d73 acme-tiny-5a7b4e7.tar.gz