Repository: Netflix/lemur

Issue:
failed to build docker, yargs parser supports a minimum Node.js version of 10.
```bash
cd  docker && sudo docker-compose up -d --build
```

```
Error: yargs parser supports a minimum Node.js version of 10. Read our version support policy: https://github.com/yargs/yargs-parser#supported-nodejs-versions
    at Object.<anonymous> (/opt/lemur/node_modules/yargs-parser/build/index.cjs:991:15)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/opt/lemur/node_modules/yargs/build/index.cjs:2855:16)
    at Module._compile (module.js:653:30)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
ERROR: Service 'lemur' failed to build: The command '/bin/sh -c npm install --unsafe-perm &&     pip3 install -e . &&     node_modules/.bin/gulp build &&     node_modules/.bin/gulp package --urlContextPath=${URLCONTEXT} &&     apk del build-dependencies' returned a non-zero code: 1
```


Discussion:

Resolving patch:
diff --git a/docker/src/lemur.conf.py b/docker/src/lemur.conf.py
--- a/docker/src/lemur.conf.py
+++ b/docker/src/lemur.conf.py
@@ -1,11 +1,18 @@
-import os
+import os.path
 import random
 import string
+from celery.schedules import crontab
+
 import base64
-from ast import literal_eval
 
 _basedir = os.path.abspath(os.path.dirname(__file__))
 
+# See the Lemur docs (https://lemur.readthedocs.org) for more information on configuration
+
+LOG_LEVEL = str(os.environ.get('LOG_LEVEL', 'DEBUG'))
+LOG_FILE = str(os.environ.get('LOG_FILE', '/home/lemur/.lemur/lemur.log'))
+LOG_JSON = True
+
 CORS = os.environ.get("CORS") == "True"
 debug = os.environ.get("DEBUG") == "True"
 
@@ -17,44 +24,214 @@ def get_random_secret(length):
     return secret_key + ''.join(random.choice(string.digits) for x in range(round(length / 4)))
 
 
+# This is the secret key used by Flask session management
 SECRET_KEY = repr(os.environ.get('SECRET_KEY', get_random_secret(32).encode('utf8')))
 
+# You should consider storing these separately from your config
 LEMUR_TOKEN_SECRET = repr(os.environ.get('LEMUR_TOKEN_SECRET',
                                          base64.b64encode(get_random_secret(32).encode('utf8'))))
+# This must match the key for whichever DB the container is using - this could be a dump of dev or test, or a unique key
 LEMUR_ENCRYPTION_KEYS = repr(os.environ.get('LEMUR_ENCRYPTION_KEYS',
-                                            base64.b64encode(get_random_secret(32).encode('utf8'))))
-
-LEMUR_ALLOWED_DOMAINS = []
-
-LEMUR_EMAIL = ''
-LEMUR_SECURITY_TEAM_EMAIL = []
-
-ALLOW_CERT_DELETION = os.environ.get('ALLOW_CERT_DELETION') == "True"
-
-LEMUR_DEFAULT_COUNTRY = str(os.environ.get('LEMUR_DEFAULT_COUNTRY',''))
-LEMUR_DEFAULT_STATE = str(os.environ.get('LEMUR_DEFAULT_STATE',''))
-LEMUR_DEFAULT_LOCATION = str(os.environ.get('LEMUR_DEFAULT_LOCATION',''))
-LEMUR_DEFAULT_ORGANIZATION = str(os.environ.get('LEMUR_DEFAULT_ORGANIZATION',''))
-LEMUR_DEFAULT_ORGANIZATIONAL_UNIT = str(os.environ.get('LEMUR_DEFAULT_ORGANIZATIONAL_UNIT',''))
-
-LEMUR_DEFAULT_ISSUER_PLUGIN = str(os.environ.get('LEMUR_DEFAULT_ISSUER_PLUGIN',''))
-LEMUR_DEFAULT_AUTHORITY = str(os.environ.get('LEMUR_DEFAULT_AUTHORITY',''))
+                                            base64.b64encode(get_random_secret(32).encode('utf8')).decode('utf8')))
+
+REDIS_HOST = 'redis'
+REDIS_PORT = 6379
+REDIS_DB = 0
+CELERY_RESULT_BACKEND = f'redis://{REDIS_HOST}:{REDIS_PORT}'
+CELERY_BROKER_URL = f'redis://{REDIS_HOST}:{REDIS_PORT}'
+CELERY_IMPORTS = ('lemur.common.celery')
+CELERYBEAT_SCHEDULE = {
+    # All tasks are disabled by default. Enable any tasks you wish to run.
+    # 'fetch_all_pending_acme_certs': {
+    #     'task': 'lemur.common.celery.fetch_all_pending_acme_certs',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(minute="*"),
+    # },
+    # 'remove_old_acme_certs': {
+    #     'task': 'lemur.common.celery.remove_old_acme_certs',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=8, minute=0, day_of_week=5),
+    # },
+    # 'clean_all_sources': {
+    #     'task': 'lemur.common.celery.clean_all_sources',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=5, minute=0, day_of_week=5),
+    # },
+    # 'sync_all_sources': {
+    #     'task': 'lemur.common.celery.sync_all_sources',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour="*/2", minute=0),
+    #     # this job is running 30min before endpoints_expire which deletes endpoints which were not updated
+    # },
+    # 'sync_source_destination': {
+    #     'task': 'lemur.common.celery.sync_source_destination',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour="*/2", minute=15),
+    # },
+    # 'report_celery_last_success_metrics': {
+    #     'task': 'lemur.common.celery.report_celery_last_success_metrics',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(minute="*"),
+    # },
+    # 'certificate_reissue': {
+    #     'task': 'lemur.common.celery.certificate_reissue',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=9, minute=0),
+    # },
+    # 'certificate_rotate': {
+    #     'task': 'lemur.common.celery.certificate_rotate',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=10, minute=0),
+    # },
+    # 'endpoints_expire': {
+    #     'task': 'lemur.common.celery.endpoints_expire',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour="*/2", minute=30),
+    #     # this job is running 30min after sync_all_sources which updates endpoints
+    # },
+    # 'get_all_zones': {
+    #     'task': 'lemur.common.celery.get_all_zones',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(minute="*/30"),
+    # },
+    # 'check_revoked': {
+    #     'task': 'lemur.common.celery.check_revoked',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=10, minute=0),
+    # }
+    # 'enable_autorotate_for_certs_attached_to_endpoint': {
+    #     'task': 'lemur.common.celery.enable_autorotate_for_certs_attached_to_endpoint',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=10, minute=0),
+    # }
+    # 'notify_expirations': {
+    #     'task': 'lemur.common.celery.notify_expirations',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=10, minute=0),
+    #  },
+    # 'notify_authority_expirations': {
+    #     'task': 'lemur.common.celery.notify_authority_expirations',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=10, minute=0),
+    # },
+    # 'send_security_expiration_summary': {
+    #     'task': 'lemur.common.celery.send_security_expiration_summary',
+    #     'options': {
+    #         'expires': 180
+    #     },
+    #     'schedule': crontab(hour=10, minute=0, day_of_week='mon-fri'),
+    # }
+}
+CELERY_TIMEZONE = 'UTC'
+
+SQLALCHEMY_ENABLE_FLASK_REPLICATED = False
+SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI', 'postgresql://lemur:lemur@localhost:5432/lemur')
+
+SQLALCHEMY_TRACK_MODIFICATIONS = False
+SQLALCHEMY_ECHO = True
+SQLALCHEMY_POOL_RECYCLE = 499
+SQLALCHEMY_POOL_TIMEOUT = 20
+
+LEMUR_EMAIL = 'lemur@example.com'
+LEMUR_SECURITY_TEAM_EMAIL = ['security@example.com']
+LEMUR_SECURITY_TEAM_EMAIL_INTERVALS = [15, 2]
+LEMUR_DEFAULT_EXPIRATION_NOTIFICATION_INTERVALS = [30, 15, 2]
+LEMUR_EMAIL_SENDER = 'smtp'
+
+# mail configuration
+# MAIL_SERVER = 'mail.example.com'
+
+PUBLIC_CA_MAX_VALIDITY_DAYS = 397
+DEFAULT_VALIDITY_DAYS = 365
+
+LEMUR_OWNER_EMAIL_IN_SUBJECT = False
+
+LEMUR_DEFAULT_COUNTRY = str(os.environ.get('LEMUR_DEFAULT_COUNTRY', 'US'))
+LEMUR_DEFAULT_STATE = str(os.environ.get('LEMUR_DEFAULT_STATE', 'California'))
+LEMUR_DEFAULT_LOCATION = str(os.environ.get('LEMUR_DEFAULT_LOCATION', 'Los Gatos'))
+LEMUR_DEFAULT_ORGANIZATION = str(os.environ.get('LEMUR_DEFAULT_ORGANIZATION', 'Example, Inc.'))
+LEMUR_DEFAULT_ORGANIZATIONAL_UNIT = str(os.environ.get('LEMUR_DEFAULT_ORGANIZATIONAL_UNIT', ''))
+
+LEMUR_DEFAULT_AUTHORITY = str(os.environ.get('LEMUR_DEFAULT_AUTHORITY', 'ExampleCa'))
+
+LEMUR_DEFAULT_ROLE = 'operator'
 
 ACTIVE_PROVIDERS = []
-
 METRIC_PROVIDERS = []
 
-LOG_LEVEL = str(os.environ.get('LOG_LEVEL','DEBUG'))
-LOG_FILE = str(os.environ.get('LOG_FILE','/home/lemur/.lemur/lemur.log'))
-
-SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI','postgresql://lemur:lemur@localhost:5432/lemur')
-
-LDAP_DEBUG = os.environ.get('LDAP_DEBUG') == "True"
-LDAP_AUTH = os.environ.get('LDAP_AUTH') == "True"
-LDAP_IS_ACTIVE_DIRECTORY = os.environ.get('LDAP_IS_ACTIVE_DIRECTORY') == "True"
-LDAP_BIND_URI = str(os.environ.get('LDAP_BIND_URI',''))
-LDAP_BASE_DN = str(os.environ.get('LDAP_BASE_DN',''))
-LDAP_EMAIL_DOMAIN = str(os.environ.get('LDAP_EMAIL_DOMAIN',''))
-LDAP_USE_TLS = str(os.environ.get('LDAP_USE_TLS',''))
-LDAP_REQUIRED_GROUP = str(os.environ.get('LDAP_REQUIRED_GROUP',''))
-LDAP_GROUPS_TO_ROLES = literal_eval(os.environ.get('LDAP_GROUPS_TO_ROLES') or "{}")
+# Authority Settings - These will change depending on which authorities you are
+# using
+current_path = os.path.dirname(os.path.realpath(__file__))
+
+# DNS Settings
+
+# exclude logging missing SAN, since we can have certs from private CAs with only cn, prod parity
+LOG_SSL_SUBJ_ALT_NAME_ERRORS = False
+
+ACME_DNS_PROVIDER_TYPES = {"items": [
+    {
+        'name': 'route53',
+        'requirements': [
+            {
+                'name': 'account_id',
+                'type': 'int',
+                'required': True,
+                'helpMessage': 'AWS Account number'
+            },
+        ]
+    },
+    {
+        'name': 'cloudflare',
+        'requirements': [
+            {
+                'name': 'email',
+                'type': 'str',
+                'required': True,
+                'helpMessage': 'Cloudflare Email'
+            },
+            {
+                'name': 'key',
+                'type': 'str',
+                'required': True,
+                'helpMessage': 'Cloudflare Key'
+            },
+        ]
+    },
+    {
+        'name': 'dyn',
+    },
+    {
+        'name': 'ultradns',
+    },
+]}
+
+# Authority plugins which support revocation
+SUPPORTED_REVOCATION_AUTHORITY_PLUGINS = ['acme-issuer']


Test patch: