Skip to content

Commit 884b840

Browse files
Added celery task for updating certificate. Added crontab with job fir this.
1 parent 74b6d12 commit 884b840

File tree

6 files changed

+132
-6
lines changed

6 files changed

+132
-6
lines changed

conf_templates/crontab

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Edit this file to introduce tasks to be run by cron.
2+
#
3+
# Each task to run has to be defined through a single line
4+
# indicating with different fields when the task will be run
5+
# and what command to run for the task
6+
#
7+
# To define the time you can provide concrete values for
8+
# minute (m), hour (h), day of month (dom), month (mon),
9+
# and day of week (dow) or use '*' in these fields (for 'any').#
10+
# Notice that tasks will be started based on the cron's system
11+
# daemon's notion of time and timezones.
12+
#
13+
# Output of the crontab jobs (including errors) is sent through
14+
# email to the user the crontab file belongs to (unless redirected).
15+
#
16+
# For example, you can run a backup of all your user accounts
17+
# at 5 a.m every week with:
18+
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
19+
#
20+
# For more information see the manual pages of crontab(5) and cron(8)
21+
#
22+
# m h dom mon dow command
23+
SHELL=/bin/bash
24+
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
25+
26+
HOME_DIR=/home/steepshot_io/steepshot_io
27+
ACTIVATE_SCRIPT=/home/steepshot_io/.virtualenvs/steepshot_io/bin/activate
28+
POSTACTIVATE_SCRIPT=/home/steepshot_io/.virtualenvs/steepshot_io/bin/postactivate
29+
0 1 * * 1 cd $HOME_DIR && source $ACTIVATE_SCRIPT && source $POSTACTIVATE_SCRIPT && export DJANGO_SETTINGS_MODULE="steepshot_io.prod_settings" && python manage.py check_cert_expiration_date

conf_templates/postactivate

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export DJANGO_SETTINGS_MODULE=%(SETTINGS_MODULE)s
55
export DATABASE_URL=%(DATABASE_URL)s
66
export DJANGO_DEBUG=False
77
export DJANGO_LOCAL=False
8+
export DJANGO_DOMAIN=%(DOMAIN_NAME)s
89
export DJANGO_CERTBOT_CERT=%(IS_CERTBOT_CERT)s

fabfile.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
import os
2-
import logging
31
import datetime
2+
import logging
3+
import os
4+
5+
from fabric.api import env, task, sudo, prefix, cd, settings, require
6+
from fabric.contrib.files import upload_template, contains, append, exists
7+
from fabric.operations import put
48

59
from steepshot_io.deploy_settings import (
610
USER, HOST, REMOTE_DEPLOY_DIR, PROJECT_NAME, REPOSITORY,
@@ -13,9 +17,6 @@
1317
BACKEND_SERVICE, CELERY_SERVICE
1418
)
1519

16-
from fabric.api import env, task, sudo, prefix, run, cd, settings, local, require
17-
from fabric.contrib.files import upload_template, contains, append, exists
18-
1920
# This allows us to have .profile to be read when calling sudo
2021
# and virtualenvwrapper being activated using non-SSH user
2122
SUDO_PREFIX = 'sudo -i'
@@ -188,6 +189,7 @@ def config_virtualenv():
188189
'DATABASE_URL': DATABASE_URL,
189190
'SETTINGS_MODULE': env.settings_module,
190191
'IS_CERTBOT_CERT': env.is_certbot_cert,
192+
'DOMAIN_NAME': env.current_host,
191193
}
192194
upload_template(os.path.join(LOCAL_CONF_DIR, 'postactivate'),
193195
remote_postactivate_path, context=postactivate_context,
@@ -232,6 +234,22 @@ def deploy_files():
232234
sudo('git pull origin {}'.format(env.branch))
233235

234236

237+
@task
238+
def config_crontab():
239+
crontab_file = os.path.join(LOCAL_CONF_DIR, 'crontab')
240+
241+
with settings(warn_only=True):
242+
# There may be no previous crontab so
243+
# crontab will fail
244+
backup_file = '/tmp/crontab-%s' % _get_current_datetime()
245+
logger.info("Backing up existing crontab")
246+
sudo('crontab -l > %s' % backup_file)
247+
logger.info("Uploading new crontab")
248+
put(crontab_file, '/tmp/new-crontab')
249+
logger.info("Setting new crontab")
250+
sudo('crontab < /tmp/new-crontab')
251+
252+
235253
@task
236254
def clean_pyc():
237255
"""
@@ -468,6 +486,7 @@ def first_time_deploy():
468486
def deploy():
469487
require('branch', 'user', 'hosts')
470488
deploy_files()
489+
config_crontab()
471490
install_req()
472491
deploy_static()
473492
update_static_chmod()

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ six==1.10.0
2222
asyncio==3.4.3
2323
aiohttp==1.3.5
2424
aiohttp-wsgi==0.6.6
25+
26+
# For automation check of certificate expiration
27+
pyOpenSSL==17.2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from django.core.management import BaseCommand
2+
3+
from steepshot_io.core import tasks
4+
5+
6+
class Command(BaseCommand):
7+
help = "Get cert expiration date"
8+
9+
def handle(self, *args, **options):
10+
tasks.check_cert_expiration_date.delay()

steepshot_io/core/tasks.py

+65-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,67 @@
1-
import datetime
1+
import os
2+
import ssl
3+
import subprocess
4+
from datetime import datetime, timedelta
5+
from ssl import SSLError, SSLZeroReturnError, SSLEOFError, CertificateError
26

7+
import OpenSSL
8+
import dateutil.parser
39
from celery import task
10+
11+
12+
@task()
13+
def check_cert_expiration_date():
14+
# WARNING: The user from which this task is launched should be added to the sudoers.
15+
# For example:
16+
# <username> ALL = (ALL:ALL) NOPASSWD: /bin/systemctl stop nginx.service
17+
# <username> ALL = (ALL:ALL) NOPASSWD: /bin/systemctl start nginx.service
18+
# <username> ALL = (ALL:ALL) NOPASSWD: /usr/bin/certbot renew
19+
20+
django_certbot_cert = os.getenv('DJANGO_CERTBOT_CERT')
21+
if django_certbot_cert == 'False' or django_certbot_cert is None:
22+
print("CertBot certificate not used!")
23+
return
24+
elif django_certbot_cert == 'True':
25+
def get_days_left():
26+
try:
27+
cert = ssl.get_server_certificate((os.getenv('DJANGO_DOMAIN'), 443))
28+
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
29+
days_left = dateutil.parser.parse(x509.get_notAfter()).date() - datetime.now().date()
30+
return days_left
31+
except (SSLError, SSLEOFError,
32+
SSLZeroReturnError, CertificateError,
33+
ConnectionAbortedError, ConnectionError,
34+
ConnectionRefusedError, ConnectionResetError) as e:
35+
print('Got the following error during to get server certificate: {error}'.format(error=e))
36+
return timedelta(days=100)
37+
38+
if get_days_left() < timedelta(days=7):
39+
print("Certificate will expire soon! Performing certificate renewal.")
40+
41+
nginx_process = subprocess.run(['sudo', 'systemctl', 'stop', 'nginx.service'],
42+
stdout=subprocess.PIPE)
43+
if nginx_process.returncode != 0:
44+
print("Nginx service has not been stopped successfully!")
45+
print(nginx_process.stdout)
46+
return
47+
48+
certbot_process = subprocess.run(['sudo', 'certbot', 'renew'],
49+
stdout=subprocess.PIPE)
50+
if certbot_process.returncode == 0:
51+
print("Certificate has been renewed!")
52+
print("{days} days left when certificate will expire.".format(days=get_days_left().days))
53+
else:
54+
print("Certificate has not been renewed for reason below!")
55+
print(certbot_process.stdout)
56+
57+
nginx_process = subprocess.run(['sudo', 'systemctl', 'start', 'nginx.service'],
58+
stdout=subprocess.PIPE)
59+
60+
if nginx_process.returncode != 0:
61+
print("Nginx service has not been started successfully!")
62+
print(nginx_process.stdout)
63+
return
64+
65+
print("The 'check_cert_expiration_date' task has been successfully completed!")
66+
else:
67+
print("{days} days left when certificate will expire.".format(days=get_days_left().days))

0 commit comments

Comments
 (0)