Done !
| Server IP : 192.145.236.177 / Your IP : 216.73.216.238 Web Server : Apache System : Linux ded5817.inmotionhosting.com 5.14.0-687.26.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Jul 14 16:32:02 EDT 2026 x86_64 User : personalinjurynh ( 1013) PHP Version : 8.1.34 Disable Function : exec,passthru,shell_exec,system MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /sbin/ |
Upload File : |
#!/opt/imh-python/bin/python3
"""
nginx-validate
NGINX config validator/auto-fixer
Checks and optionally fixes any duplicate directives within
the http{} context
@author Jacob Hipps <jacobh@inmotionhosting.com>
Copyright (c) 2020-2022 InMotion Hosting, Inc.
"""
import os
import sys
import re
import logging
import logging.handlers
from argparse import ArgumentParser
from glob import glob
import crossplane
IGNORE_DIRECTIVES = r'^(server|set_real_ip_from|geoip2|geo|map|types)$'
IGNORE_INCLUDES = r'^.+/(mime\.types|vhosts/.*)$'
DIRECTIVE_DUPE_OK = ('include', 'set_real_ip_from', 'map', 'proxy_cache_path', 'allow', 'deny', 'types')
logger = logging.getLogger('nginx-validate')
def setup_logging(clevel=logging.INFO, flevel=logging.DEBUG, logfile='/var/log/nginx-validate.log'):
"""
Setup logging
"""
logger.setLevel(logging.DEBUG)
# Console
con = logging.StreamHandler()
con.setLevel(clevel)
con_format = logging.Formatter("%(levelname)s: %(message)s")
con.setFormatter(con_format)
logger.addHandler(con)
# File
try:
flog = logging.handlers.WatchedFileHandler(logfile)
flog.setLevel(flevel)
flog_format = logging.Formatter("[%(asctime)s] %(name)s: %(levelname)s: %(message)s")
flog.setFormatter(flog_format)
logger.addHandler(flog)
except Exception as e:
logger.warning("Failed to open logfile: %s", str(e))
def parse_cli(show_help=False):
"""
Parse CLI arguments
"""
parser = ArgumentParser(description="imh-nginx configuration validator")
parser.set_defaults(configpath='/etc/nginx/nginx.conf', fix=False, override=False,
loglevel=logging.INFO, logfile='/var/log/nginx-validate.log')
parser.add_argument('configpath', nargs='?', metavar='CONFIG_PATH',
help="Path to config file to validate [/etc/nginx/nginx.conf]")
parser.add_argument('--fix', '-f', action='store_true',
help="Automatically fix/comment-out any conflicting configuration lines")
parser.add_argument('--override', '-O', action='store_true', help="Allow overriding nginx.conf")
parser.add_argument('--logfile', '-l', metavar="PATH", help="Log output file")
parser.add_argument('--debug', '-d', dest='loglevel', action='store_const', const=logging.DEBUG,
help="Enable debug output")
if show_help:
parser.print_help()
sys.exit(1)
return parser.parse_args()
def parse_confd(conffile):
"""
Parse directives from @conffile, a file in conf.d
"""
try:
logger.debug("parse_confd: Parsing config: %s", conffile)
lex = crossplane.lex(conffile)
except Exception as e:
logger.error("Failed to parse config [%s]: %s", conffile, str(e))
return None
dlist = {}
lastline = 0
lastdir = None
inblock = 0
for ttok in lex:
dtok = ttok[0]
linenum = ttok[1]
if len(dtok) == 0:
continue
elif dtok[0] == '{':
inblock += 1
elif dtok[0] == '}':
inblock -= 1
elif inblock == 0:
if linenum != lastline:
if dtok[0] == '#':
lastdir = None
else:
if not re.match(IGNORE_DIRECTIVES, dtok):
dlist[linenum] = {'directive': dtok, 'line': linenum, 'file': conffile, 'args': []}
lastdir = ttok
else:
lastdir = None
lastline = linenum
else:
if lastdir:
if dtok[0] not in (';', '{', '}'):
dlist[linenum]['args'].append(dtok)
return dlist.values()
def parse_conf(configpath):
"""
Parse main nginx.conf from @configpath, and any included files
in the http context
"""
# parse main config
try:
logger.debug("parse_conf: Parsing top-level config: %s", configpath)
conf = crossplane.parse(configpath, single=True)
except Exception as e:
logger.error("Failed to parse config [%s]: %s", configpath, str(e))
return False
if conf['status'] != 'ok':
conf['errors']
# check directives
dlist = []
errlist = []
for tconf in conf['config'][0]['parsed']:
if tconf['directive'] == 'http':
for tdir in tconf['block']:
if tdir['directive'] == 'include':
if re.match(IGNORE_INCLUDES, tdir['args'][0]):
continue
else:
for tconf in glob(tdir['args'][0]):
# validate each included file
# if we fail to parse the file, then abort
#try:
dlist += parse_confd(tconf)
#except:
# pass
dlist.append({'directive': tdir['directive'], 'line': tdir['line'], 'file': configpath, 'args': tdir['args']})
return dlist
def fix_dupe(directive, file, line, args):
"""
Fix a duplicate directive by commenting it out
@directive, @file, @line, and @args are parameters from the config parser
"""
# read in the current config
try:
with open(file) as f:
flines = f.readlines()
except Exception as e:
logger.error("fix_dupe: failed to read config file [%s]: %s", file, str(e))
return False
# comment it out
flines[line - 1] = '# ' + flines[line - 1]
# write back to the file
try:
with open(file, 'w') as f:
f.write(''.join(flines))
logger.warning("fix_dupe: commented-out line %d in file %s", line, file)
except Exception as e:
logger.error("fix_dupe: failed to write updated config file [%s]: %s", file, str(e))
return False
return True
def validate(configpath, autofix=False, override_main=False):
"""
Validate an NGINX configuration file or set of files
@configpath is the path to the main nginx.conf
@autofix determines whether we should automatically comment-out/disable
any line that throws an error or directive conflict
"""
# parse configs into a list of dicts
# {directive, line, file, args}
conf = parse_conf(configpath)
# check for duplicate directives
dhisto = {}
dupes = []
for tdir in conf:
if tdir['directive'] in DIRECTIVE_DUPE_OK:
continue
elif tdir['directive'] in dhisto:
if dhisto[tdir['directive']]['file'] == "/etc/nginx/nginx.conf" and override_main:
baddir = dhisto[tdir['directive']]
dhisto[tdir['directive']] = tdir
else:
baddir = tdir
logger.warning("duplicate directive found: '%s %s' in %s, line %d",
tdir['directive'], " ".join(baddir['args']), baddir['file'], baddir['line'])
dupes.append(baddir)
else:
dhisto[tdir['directive']] = tdir
# if autofix is enabled, then comment-out any dupes
if len(dupes) and autofix:
for tdupe in dupes:
if not fix_dupe(**tdupe):
logger.error("failed to apply last fix; aborting operation")
return 2
return 1
elif len(dupes):
return 3
else:
return 0
def check_for_override(fpath="/etc/nginx/.enable_override"):
"""
Checks for override file, which invokes --override
automatically and allows directives in nginx.conf
to be overridden
"""
if os.path.exists(fpath):
logger.debug("Config override enabled; %s is present", fpath)
return True
return False
def _main():
"""
Entry point
"""
args = parse_cli()
setup_logging(clevel=args.loglevel, logfile=args.logfile)
vstat = validate(args.configpath, args.fix, args.override or check_for_override())
if vstat == 0:
print("OK: Config validated")
return 0
elif vstat == 1:
print("WARNING: Configuration fixed; check log file %s for details" % (args.logfile))
return 0
elif vstat == 2:
print("ERROR: Configuration fix failed; check log file %s for details" % (args.logfile))
return 2
else:
print("ERROR: config validation failed")
return 1
if __name__ == '__main__':
sys.exit(_main())