certbot-nginx-0.23.0/0000755000076600000240000000000013261245052014270 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/PKG-INFO0000644000076600000240000000224613261245052015371 0ustar bmwstaff00000000000000Metadata-Version: 2.1 Name: certbot-nginx Version: 0.23.0 Summary: Nginx plugin for Certbot Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project Author-email: client-dev@letsencrypt.org License: Apache License 2.0 Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Plugins Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Security Classifier: Topic :: System :: Installation/Setup Classifier: Topic :: System :: Networking Classifier: Topic :: System :: Systems Administration Classifier: Topic :: Utilities Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* Provides-Extra: docs certbot-nginx-0.23.0/certbot_nginx.egg-info/0000755000076600000240000000000013261245052020627 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx.egg-info/PKG-INFO0000644000076600000240000000224613261245052021730 0ustar bmwstaff00000000000000Metadata-Version: 2.1 Name: certbot-nginx Version: 0.23.0 Summary: Nginx plugin for Certbot Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project Author-email: client-dev@letsencrypt.org License: Apache License 2.0 Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Plugins Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Security Classifier: Topic :: System :: Installation/Setup Classifier: Topic :: System :: Networking Classifier: Topic :: System :: Systems Administration Classifier: Topic :: Utilities Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* Provides-Extra: docs certbot-nginx-0.23.0/certbot_nginx.egg-info/SOURCES.txt0000644000076600000240000000637613261245052022527 0ustar bmwstaff00000000000000LICENSE.txt MANIFEST.in README.rst setup.cfg setup.py certbot_nginx/__init__.py certbot_nginx/configurator.py certbot_nginx/constants.py certbot_nginx/display_ops.py certbot_nginx/http_01.py certbot_nginx/nginxparser.py certbot_nginx/obj.py certbot_nginx/options-ssl-nginx.conf certbot_nginx/parser.py certbot_nginx/tls_sni_01.py certbot_nginx.egg-info/PKG-INFO certbot_nginx.egg-info/SOURCES.txt certbot_nginx.egg-info/dependency_links.txt certbot_nginx.egg-info/entry_points.txt certbot_nginx.egg-info/requires.txt certbot_nginx.egg-info/top_level.txt certbot_nginx/tests/__init__.py certbot_nginx/tests/configurator_test.py certbot_nginx/tests/display_ops_test.py certbot_nginx/tests/http_01_test.py certbot_nginx/tests/nginxparser_test.py certbot_nginx/tests/obj_test.py certbot_nginx/tests/parser_test.py certbot_nginx/tests/tls_sni_01_test.py certbot_nginx/tests/util.py certbot_nginx/tests/testdata/etc_nginx/broken.conf certbot_nginx/tests/testdata/etc_nginx/comment_in_file.conf certbot_nginx/tests/testdata/etc_nginx/edge_cases.conf certbot_nginx/tests/testdata/etc_nginx/foo.conf certbot_nginx/tests/testdata/etc_nginx/mime.types certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf certbot_nginx/tests/testdata/etc_nginx/multiline_quotes.conf certbot_nginx/tests/testdata/etc_nginx/nginx.conf certbot_nginx/tests/testdata/etc_nginx/server.conf certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default certbot_nginx/tests/testdata/etc_nginx/sites-enabled/example.com certbot_nginx/tests/testdata/etc_nginx/sites-enabled/globalssl.com certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6.com certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com certbot_nginx/tests/testdata/etc_nginx/sites-enabled/migration.com certbot_nginx/tests/testdata/etc_nginx/sites-enabled/sslon.com certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default docs/.gitignore docs/Makefile docs/api.rst docs/conf.py docs/index.rst docs/make.bat docs/_static/.gitignore docs/_templates/.gitignore docs/api/nginxparser.rst docs/api/obj.rst docs/api/parser.rst docs/api/tls_sni_01.rstcertbot-nginx-0.23.0/certbot_nginx.egg-info/entry_points.txt0000644000076600000240000000011013261245052024115 0ustar bmwstaff00000000000000[certbot.plugins] nginx = certbot_nginx.configurator:NginxConfigurator certbot-nginx-0.23.0/certbot_nginx.egg-info/requires.txt0000644000076600000240000000017213261245052023227 0ustar bmwstaff00000000000000acme>0.21.1 certbot>0.21.1 mock PyOpenSSL pyparsing>=1.5.5 setuptools zope.interface [docs] Sphinx>=1.0 sphinx_rtd_theme certbot-nginx-0.23.0/certbot_nginx.egg-info/top_level.txt0000644000076600000240000000001613261245052023356 0ustar bmwstaff00000000000000certbot_nginx certbot-nginx-0.23.0/certbot_nginx.egg-info/dependency_links.txt0000644000076600000240000000000113261245052024675 0ustar bmwstaff00000000000000 certbot-nginx-0.23.0/MANIFEST.in0000644000076600000240000000023613261244762016036 0ustar bmwstaff00000000000000include LICENSE.txt include README.rst recursive-include docs * recursive-include certbot_nginx/tests/testdata * include certbot_nginx/options-ssl-nginx.conf certbot-nginx-0.23.0/docs/0000755000076600000240000000000013261245052015220 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/docs/index.rst0000644000076600000240000000102313261244762017064 0ustar bmwstaff00000000000000.. certbot-nginx documentation master file, created by sphinx-quickstart on Sun Oct 18 13:39:39 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to certbot-nginx's documentation! ============================================= Contents: .. toctree:: :maxdepth: 2 .. toctree:: :maxdepth: 1 api .. automodule:: certbot_nginx :members: Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` certbot-nginx-0.23.0/docs/_templates/0000755000076600000240000000000013261245052017355 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/docs/_templates/.gitignore0000644000076600000240000000000013261244762021342 0ustar bmwstaff00000000000000certbot-nginx-0.23.0/docs/Makefile0000644000076600000240000001641513261244762016676 0ustar bmwstaff00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-nginx.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-nginx.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/certbot-nginx" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-nginx" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." certbot-nginx-0.23.0/docs/conf.py0000644000076600000240000002403413261244762016531 0ustar bmwstaff00000000000000# -*- coding: utf-8 -*- # # certbot-nginx documentation build configuration file, created by # sphinx-quickstart on Sun Oct 18 13:39:39 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import shlex here = os.path.abspath(os.path.dirname(__file__)) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', ] autodoc_member_order = 'bysource' autodoc_default_flags = ['show-inheritance', 'private-members'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'certbot-nginx' copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0' # The full version, including alpha/beta/rc tags. release = '0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # otherwise, readthedocs.org uses their theme by default, so no need to specify it # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'certbot-nginxdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'certbot-nginx.tex', u'certbot-nginx Documentation', u'Let\'s Encrypt Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'certbot-nginx', u'certbot-nginx Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'certbot-nginx', u'certbot-nginx Documentation', author, 'certbot-nginx', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), 'certbot': ('https://certbot.eff.org/docs/', None), } certbot-nginx-0.23.0/docs/_static/0000755000076600000240000000000013261245052016646 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/docs/_static/.gitignore0000644000076600000240000000000013261244762020633 0ustar bmwstaff00000000000000certbot-nginx-0.23.0/docs/make.bat0000644000076600000240000001613213261244762016637 0ustar bmwstaff00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 2> nul if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\certbot-nginx.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\certbot-nginx.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end certbot-nginx-0.23.0/docs/.gitignore0000644000076600000240000000001113261244762017207 0ustar bmwstaff00000000000000/_build/ certbot-nginx-0.23.0/docs/api/0000755000076600000240000000000013261245052015771 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/docs/api/parser.rst0000644000076600000240000000015713261244762020031 0ustar bmwstaff00000000000000:mod:`certbot_nginx.parser` ------------------------------- .. automodule:: certbot_nginx.parser :members: certbot-nginx-0.23.0/docs/api/tls_sni_01.rst0000644000076600000240000000017313261244762020506 0ustar bmwstaff00000000000000:mod:`certbot_nginx.tls_sni_01` ----------------------------------- .. automodule:: certbot_nginx.tls_sni_01 :members: certbot-nginx-0.23.0/docs/api/nginxparser.rst0000644000076600000240000000017613261244762021076 0ustar bmwstaff00000000000000:mod:`certbot_nginx.nginxparser` ------------------------------------ .. automodule:: certbot_nginx.nginxparser :members: certbot-nginx-0.23.0/docs/api/obj.rst0000644000076600000240000000014613261244762017305 0ustar bmwstaff00000000000000:mod:`certbot_nginx.obj` ---------------------------- .. automodule:: certbot_nginx.obj :members: certbot-nginx-0.23.0/docs/api.rst0000644000076600000240000000013113261244762016525 0ustar bmwstaff00000000000000================= API Documentation ================= .. toctree:: :glob: api/** certbot-nginx-0.23.0/setup.py0000644000076600000240000000421013261244762016006 0ustar bmwstaff00000000000000import sys from setuptools import setup from setuptools import find_packages version = '0.23.0' # Remember to update local-oldest-requirements.txt when changing the minimum # acme/certbot version. install_requires = [ # This plugin works with an older version of acme, but Certbot does not. # 0.22.0 is specified here to work around # https://github.com/pypa/pip/issues/988. 'acme>0.21.1', 'certbot>0.21.1', 'mock', 'PyOpenSSL', 'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary? 'setuptools', 'zope.interface', ] docs_extras = [ 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 'sphinx_rtd_theme', ] setup( name='certbot-nginx', version=version, description="Nginx plugin for Certbot", url='https://github.com/letsencrypt/letsencrypt', author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', classifiers=[ 'Development Status :: 3 - Alpha', 'Environment :: Plugins', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: Apache Software License', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', 'Topic :: System :: Installation/Setup', 'Topic :: System :: Networking', 'Topic :: System :: Systems Administration', 'Topic :: Utilities', ], packages=find_packages(), include_package_data=True, install_requires=install_requires, extras_require={ 'docs': docs_extras, }, entry_points={ 'certbot.plugins': [ 'nginx = certbot_nginx.configurator:NginxConfigurator', ], }, test_suite='certbot_nginx', ) certbot-nginx-0.23.0/setup.cfg0000644000076600000240000000010313261245052016103 0ustar bmwstaff00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 certbot-nginx-0.23.0/README.rst0000644000076600000240000000003113261244762015760 0ustar bmwstaff00000000000000Nginx plugin for Certbot certbot-nginx-0.23.0/LICENSE.txt0000644000076600000240000002732013261244762016126 0ustar bmwstaff00000000000000 Copyright 2015 Electronic Frontier Foundation and others Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Incorporating code from nginxparser Copyright 2014 Fatih Erikli Licensed MIT Text of Apache License ====================== Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Text of MIT License =================== Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. certbot-nginx-0.23.0/certbot_nginx/0000755000076600000240000000000013261245052017135 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/options-ssl-nginx.conf0000644000076600000240000000216713261244762023434 0ustar bmwstaff00000000000000# This file contains important security parameters. If you modify this file # manually, Certbot will be unable to automatically provide future security # updates. Instead, Certbot will print and log an error message with a path to # the up-to-date file that you will need to refer to when manually updating # this file. ssl_session_cache shared:le_nginx_SSL:1m; ssl_session_timeout 1440m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"; certbot-nginx-0.23.0/certbot_nginx/nginxparser.py0000644000076600000240000002164713261244762022070 0ustar bmwstaff00000000000000"""Very low-level nginx config parser based on pyparsing.""" # Forked from https://github.com/fatiherikli/nginxparser (MIT Licensed) import copy import logging from pyparsing import ( Literal, White, Forward, Group, Optional, OneOrMore, QuotedString, Regex, ZeroOrMore, Combine) from pyparsing import stringEnd from pyparsing import restOfLine import six logger = logging.getLogger(__name__) class RawNginxParser(object): # pylint: disable=expression-not-assigned # pylint: disable=pointless-statement """A class that parses nginx configuration with pyparsing.""" # constants space = Optional(White()).leaveWhitespace() required_space = White().leaveWhitespace() left_bracket = Literal("{").suppress() right_bracket = space + Literal("}").suppress() semicolon = Literal(";").suppress() dquoted = QuotedString('"', multiline=True, unquoteResults=False, escChar='\\') squoted = QuotedString("'", multiline=True, unquoteResults=False, escChar='\\') quoted = dquoted | squoted head_tokenchars = Regex(r"[^{};\s'\"]") # if (last_space) tail_tokenchars = Regex(r"(\$\{)|[^{;\s]") # else tokenchars = Combine(head_tokenchars + ZeroOrMore(tail_tokenchars)) paren_quote_extend = Combine(quoted + Literal(')') + ZeroOrMore(tail_tokenchars)) # note: ')' allows extension, but then we fall into else, not last_space. token = paren_quote_extend | tokenchars | quoted whitespace_token_group = space + token + ZeroOrMore(required_space + token) + space assignment = whitespace_token_group + semicolon comment = space + Literal('#') + restOfLine block = Forward() # order matters! see issue 518, and also http { # server { \n} contents = Group(comment) | Group(block) | Group(assignment) block_begin = Group(whitespace_token_group) block_innards = Group(ZeroOrMore(contents) + space).leaveWhitespace() block << block_begin + left_bracket + block_innards + right_bracket script = OneOrMore(contents) + space + stringEnd script.parseWithTabs().leaveWhitespace() def __init__(self, source): self.source = source def parse(self): """Returns the parsed tree.""" return self.script.parseString(self.source) def as_list(self): """Returns the parsed tree as a list.""" return self.parse().asList() class RawNginxDumper(object): # pylint: disable=too-few-public-methods """A class that dumps nginx configuration from the provided tree.""" def __init__(self, blocks): self.blocks = blocks def __iter__(self, blocks=None): """Iterates the dumped nginx content.""" blocks = blocks or self.blocks for b0 in blocks: if isinstance(b0, six.string_types): yield b0 continue item = copy.deepcopy(b0) if spacey(item[0]): yield item.pop(0) # indentation if not item: continue if isinstance(item[0], list): # block yield "".join(item.pop(0)) + '{' for parameter in item.pop(0): for line in self.__iter__([parameter]): # negate "for b0 in blocks" yield line yield '}' else: # not a block - list of strings semicolon = ";" if isinstance(item[0], six.string_types) and item[0].strip() == '#': # comment semicolon = "" yield "".join(item) + semicolon def __str__(self): """Return the parsed block as a string.""" return ''.join(self) # Shortcut functions to respect Python's serialization interface # (like pyyaml, picker or json) def loads(source): """Parses from a string. :param str source: The string to parse :returns: The parsed tree :rtype: list """ return UnspacedList(RawNginxParser(source).as_list()) def load(_file): """Parses from a file. :param file _file: The file to parse :returns: The parsed tree :rtype: list """ return loads(_file.read()) def dumps(blocks): """Dump to a string. :param UnspacedList block: The parsed tree :param int indentation: The number of spaces to indent :rtype: str """ return str(RawNginxDumper(blocks.spaced)) def dump(blocks, _file): """Dump to a file. :param UnspacedList block: The parsed tree :param file _file: The file to dump to :param int indentation: The number of spaces to indent :rtype: NoneType """ return _file.write(dumps(blocks)) spacey = lambda x: (isinstance(x, six.string_types) and x.isspace()) or x == '' class UnspacedList(list): """Wrap a list [of lists], making any whitespace entries magically invisible""" def __init__(self, list_source): # ensure our argument is not a generator, and duplicate any sublists self.spaced = copy.deepcopy(list(list_source)) self.dirty = False # Turn self into a version of the source list that has spaces removed # and all sub-lists also UnspacedList()ed list.__init__(self, list_source) for i, entry in reversed(list(enumerate(self))): if isinstance(entry, list): sublist = UnspacedList(entry) list.__setitem__(self, i, sublist) self.spaced[i] = sublist.spaced elif spacey(entry): # don't delete comments if "#" not in self[:i]: list.__delitem__(self, i) def _coerce(self, inbound): """ Coerce some inbound object to be appropriately usable in this object :param inbound: string or None or list or UnspacedList :returns: (coerced UnspacedList or string or None, spaced equivalent) :rtype: tuple """ if not isinstance(inbound, list): # str or None return (inbound, inbound) else: if not hasattr(inbound, "spaced"): inbound = UnspacedList(inbound) return (inbound, inbound.spaced) def insert(self, i, x): item, spaced_item = self._coerce(x) slicepos = self._spaced_position(i) if i < len(self) else len(self.spaced) self.spaced.insert(slicepos, spaced_item) if not spacey(item): list.insert(self, i, item) self.dirty = True def append(self, x): item, spaced_item = self._coerce(x) self.spaced.append(spaced_item) if not spacey(item): list.append(self, item) self.dirty = True def extend(self, x): item, spaced_item = self._coerce(x) self.spaced.extend(spaced_item) list.extend(self, item) self.dirty = True def __add__(self, other): l = copy.deepcopy(self) l.extend(other) l.dirty = True return l def pop(self, _i=None): raise NotImplementedError("UnspacedList.pop() not yet implemented") def remove(self, _): raise NotImplementedError("UnspacedList.remove() not yet implemented") def reverse(self): raise NotImplementedError("UnspacedList.reverse() not yet implemented") def sort(self, _cmp=None, _key=None, _Rev=None): raise NotImplementedError("UnspacedList.sort() not yet implemented") def __setslice__(self, _i, _j, _newslice): raise NotImplementedError("Slice operations on UnspacedLists not yet implemented") def __setitem__(self, i, value): if isinstance(i, slice): raise NotImplementedError("Slice operations on UnspacedLists not yet implemented") item, spaced_item = self._coerce(value) self.spaced.__setitem__(self._spaced_position(i), spaced_item) if not spacey(item): list.__setitem__(self, i, item) self.dirty = True def __delitem__(self, i): self.spaced.__delitem__(self._spaced_position(i)) list.__delitem__(self, i) self.dirty = True def __deepcopy__(self, memo): new_spaced = copy.deepcopy(self.spaced, memo=memo) l = UnspacedList(new_spaced) l.dirty = self.dirty return l def is_dirty(self): """Recurse through the parse tree to figure out if any sublists are dirty""" if self.dirty: return True return any((isinstance(x, list) and x.is_dirty() for x in self)) def _spaced_position(self, idx): "Convert from indexes in the unspaced list to positions in the spaced one" pos = spaces = 0 # Normalize indexes like list[-1] etc, and save the result if idx < 0: idx = len(self) + idx if not 0 <= idx < len(self): raise IndexError("list index out of range") idx0 = idx # Count the number of spaces in the spaced list before idx in the unspaced one while idx != -1: if spacey(self.spaced[pos]): spaces += 1 else: idx -= 1 pos += 1 return idx0 + spaces certbot-nginx-0.23.0/certbot_nginx/tls_sni_01.py0000644000076600000240000001513513261244762021476 0ustar bmwstaff00000000000000"""A class that performs TLS-SNI-01 challenges for Nginx""" import logging import os import six from certbot import errors from certbot.plugins import common from certbot_nginx import obj from certbot_nginx import nginxparser logger = logging.getLogger(__name__) class NginxTlsSni01(common.TLSSNI01): """TLS-SNI-01 authenticator for Nginx :ivar configurator: NginxConfigurator object :type configurator: :class:`~nginx.configurator.NginxConfigurator` :ivar list achalls: Annotated class:`~certbot.achallenges.KeyAuthorizationAnnotatedChallenge` challenges :param list indices: Meant to hold indices of challenges in a larger array. NginxTlsSni01 is capable of solving many challenges at once which causes an indexing issue within NginxConfigurator who must return all responses in order. Imagine NginxConfigurator maintaining state about where all of the http-01 Challenges, TLS-SNI-01 Challenges belong in the response array. This is an optional utility. :param str challenge_conf: location of the challenge config file """ def perform(self): """Perform a challenge on Nginx. :returns: list of :class:`certbot.acme.challenges.TLSSNI01Response` :rtype: list """ if not self.achalls: return [] addresses = [] default_addr = "{0} ssl".format( self.configurator.config.tls_sni_01_port) ipv6, ipv6only = self.configurator.ipv6_info( self.configurator.config.tls_sni_01_port) for achall in self.achalls: vhosts = self.configurator.choose_vhosts(achall.domain, create_if_no_match=True) # len is max 1 because Nginx doesn't authenticate wildcards if vhosts and vhosts[0].addrs: addresses.append(list(vhosts[0].addrs)) else: if ipv6: # If IPv6 is active in Nginx configuration ipv6_addr = "[::]:{0} ssl".format( self.configurator.config.tls_sni_01_port) if not ipv6only: # If ipv6only=on is not already present in the config ipv6_addr = ipv6_addr + " ipv6only=on" addresses.append([obj.Addr.fromstring(default_addr), obj.Addr.fromstring(ipv6_addr)]) logger.info(("Using default addresses %s and %s for " + "TLSSNI01 authentication."), default_addr, ipv6_addr) else: addresses.append([obj.Addr.fromstring(default_addr)]) logger.info("Using default address %s for TLSSNI01 authentication.", default_addr) # Create challenge certs responses = [self._setup_challenge_cert(x) for x in self.achalls] # Set up the configuration self._mod_config(addresses) # Save reversible changes self.configurator.save("SNI Challenge", True) return responses def _mod_config(self, ll_addrs): """Modifies Nginx config to include challenge server blocks. :param list ll_addrs: list of lists of :class:`certbot_nginx.obj.Addr` to apply :raises .MisconfigurationError: Unable to find a suitable HTTP block in which to include authenticator hosts. """ # Add the 'include' statement for the challenges if it doesn't exist # already in the main config included = False include_directive = ['\n', 'include', ' ', self.challenge_conf] root = self.configurator.parser.config_root bucket_directive = ['\n', 'server_names_hash_bucket_size', ' ', '128'] main = self.configurator.parser.parsed[root] for line in main: if line[0] == ['http']: body = line[1] found_bucket = False posn = 0 for inner_line in body: if inner_line[0] == bucket_directive[1]: if int(inner_line[1]) < int(bucket_directive[3]): body[posn] = bucket_directive found_bucket = True posn += 1 if not found_bucket: body.insert(0, bucket_directive) if include_directive not in body: body.insert(0, include_directive) included = True break if not included: raise errors.MisconfigurationError( 'Certbot could not find an HTTP block to include ' 'TLS-SNI-01 challenges in %s.' % root) config = [self._make_server_block(pair[0], pair[1]) for pair in six.moves.zip(self.achalls, ll_addrs)] config = nginxparser.UnspacedList(config) self.configurator.reverter.register_file_creation( True, self.challenge_conf) with open(self.challenge_conf, "w") as new_conf: nginxparser.dump(config, new_conf) def _make_server_block(self, achall, addrs): """Creates a server block for a challenge. :param achall: Annotated TLS-SNI-01 challenge :type achall: :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge` :param list addrs: addresses of challenged domain :class:`list` of type :class:`~nginx.obj.Addr` :returns: server block for the challenge host :rtype: list """ document_root = os.path.join( self.configurator.config.work_dir, "tls_sni_01_page") block = [['listen', ' ', addr.to_string(include_default=False)] for addr in addrs] block.extend([['server_name', ' ', achall.response(achall.account_key).z_domain.decode('ascii')], # access and error logs necessary for # integration testing (non-root) ['access_log', ' ', os.path.join( self.configurator.config.work_dir, 'access.log')], ['error_log', ' ', os.path.join( self.configurator.config.work_dir, 'error.log')], ['ssl_certificate', ' ', self.get_cert_path(achall)], ['ssl_certificate_key', ' ', self.get_key_path(achall)], ['include', ' ', self.configurator.mod_ssl_conf], [['location', ' ', '/'], [['root', ' ', document_root]]]]) return [['server'], block] certbot-nginx-0.23.0/certbot_nginx/constants.py0000644000076600000240000000337513261244762021542 0ustar bmwstaff00000000000000"""nginx plugin constants.""" import pkg_resources CLI_DEFAULTS = dict( server_root="/etc/nginx", ctl="nginx", ) """CLI defaults.""" MOD_SSL_CONF_DEST = "options-ssl-nginx.conf" """Name of the mod_ssl config file as saved in `IConfig.config_dir`.""" MOD_SSL_CONF_SRC = pkg_resources.resource_filename( "certbot_nginx", "options-ssl-nginx.conf") """Path to the nginx mod_ssl config file found in the Certbot distribution.""" UPDATED_MOD_SSL_CONF_DIGEST = ".updated-options-ssl-nginx-conf-digest.txt" """Name of the hash of the updated or informed mod_ssl_conf as saved in `IConfig.config_dir`.""" ALL_SSL_OPTIONS_HASHES = [ '0f81093a1465e3d4eaa8b0c14e77b2a2e93568b0fc1351c2b87893a95f0de87c', '9a7b32c49001fed4cff8ad24353329472a50e86ade1ef9b2b9e43566a619612e', 'a6d9f1c7d6b36749b52ba061fff1421f9a0a3d2cfdafbd63c05d06f65b990937', '7f95624dd95cf5afc708b9f967ee83a24b8025dc7c8d9df2b556bbc64256b3ff', '394732f2bbe3e5e637c3fb5c6e980a1f1b90b01e2e8d6b7cff41dde16e2a756d', '4b16fec2bcbcd8a2f3296d886f17f9953ffdcc0af54582452ca1e52f5f776f16', ] """SHA256 hashes of the contents of all versions of MOD_SSL_CONF_SRC""" def os_constant(key): # XXX TODO: In the future, this could return different constants # based on what OS we are running under. To see an # approach to how to handle different OSes, see the # apache version of this file. Currently, we do not # actually have any OS-specific constants on Nginx. """ Get a constant value for operating system :param key: name of cli constant :return: value of constant for active os """ return CLI_DEFAULTS[key] HSTS_ARGS = ['\"max-age=31536000\"', ' ', 'always'] HEADER_ARGS = {'Strict-Transport-Security': HSTS_ARGS} certbot-nginx-0.23.0/certbot_nginx/tests/0000755000076600000240000000000013261245052020277 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/0000755000076600000240000000000013261245052022110 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/0000755000076600000240000000000013261245052024066 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/0000755000076600000240000000000013261245052027503 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/0000755000076600000240000000000013261245052032352 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/0000755000076600000240000000000013261245052033475 5ustar bmwstaff00000000000000././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_paramscertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/p0000644000076600000240000000026413261244762033670 0ustar bmwstaff00000000000000proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; ././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/n0000644000076600000240000000033613261244762033666 0ustar bmwstaff00000000000000[nx_extract] username = naxsi_web password = test port = 8081 rules_path = /etc/nginx/naxsi_core.rules [nx_intercept] port = 8080 [sql] dbtype = sqlite username = root password = hostname = 127.0.0.1 dbname = naxsi_sig ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/s0000755000076600000240000000000013261245052033660 5ustar bmwstaff00000000000000././@LongLink0000000000000000000000000000017100000000000011214 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/defaultcertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/s0000644000076600000240000000504113261244762033671 0ustar bmwstaff00000000000000# You may add here your # server { # ... # } # statements for each of your virtual hosts to this file ## # You should look at the following URL's in order to grasp a solid understanding # of Nginx configuration files in order to fully unleash the power of Nginx. # http://wiki.nginx.org/Pitfalls # http://wiki.nginx.org/QuickStart # http://wiki.nginx.org/Configuration # # Generally, you will want to move this file somewhere, and start with a clean # file but keep this around for reference. Or just disable in sites-enabled. # # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. ## server { listen 80 default_server; listen [::]:80 default_server ipv6only=on; root /usr/share/nginx/html; index index.html index.htm; # Make site accessible from http://localhost/ server_name localhost; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; # Uncomment to enable naxsi on this location # include /etc/nginx/naxsi.rules } # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests #location /RequestDenied { # proxy_pass http://127.0.0.1:8080; #} #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # #error_page 500 502 503 504 /50x.html; #location = /50x.html { # root /usr/share/nginx/html; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # fastcgi_split_path_info ^(.+\.php)(/.+)$; # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini # # # With php5-cgi alone: # fastcgi_pass 127.0.0.1:9000; # # With php5-fpm: # fastcgi_pass unix:/var/run/php5-fpm.sock; # fastcgi_index index.php; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # root html; # index index.html index.htm; # # location / { # try_files $uri $uri/ =404; # } #} # HTTPS server # #server { # listen 443; # server_name localhost; # # root html; # index index.html index.htm; # # ssl on; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # # ssl_session_timeout 5m; # # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; # ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; # ssl_prefer_server_ciphers on; # # location / { # try_files $uri $uri/ =404; # } #} ././@LongLink0000000000000000000000000000016200000000000011214 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_paramscertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/f0000644000076600000240000000161713261244762033661 0ustar bmwstaff00000000000000fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_FILENAME $request_filename; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; fastcgi_param HTTPS $https if_not_empty; # PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200; ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_paramscertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/s0000644000076600000240000000072113261244762033671 0ustar bmwstaff00000000000000scgi_param REQUEST_METHOD $request_method; scgi_param REQUEST_URI $request_uri; scgi_param QUERY_STRING $query_string; scgi_param CONTENT_TYPE $content_type; scgi_param DOCUMENT_URI $document_uri; scgi_param DOCUMENT_ROOT $document_root; scgi_param SCGI 1; scgi_param SERVER_PROTOCOL $server_protocol; scgi_param REMOTE_ADDR $remote_addr; scgi_param REMOTE_PORT $remote_port; scgi_param SERVER_PORT $server_port; scgi_param SERVER_NAME $server_name; ././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rulescertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/n0000644000076600000240000000043713261244762033670 0ustar bmwstaff00000000000000# Sample rules file for default vhost. LearningMode; SecRulesEnabled; #SecRulesDisabled; DeniedUrl "/RequestDenied"; ## check rules CheckRule "$SQL >= 8" BLOCK; CheckRule "$RFI >= 8" BLOCK; CheckRule "$TRAVERSAL >= 4" BLOCK; CheckRule "$EVADE >= 4" BLOCK; CheckRule "$XSS >= 8" BLOCK; ././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_paramscertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/u0000644000076600000240000000102413261244762033670 0ustar bmwstaff00000000000000uwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param UWSGI_SCHEME $scheme; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name; ././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/s0000755000076600000240000000000013261245052033660 5ustar bmwstaff00000000000000././@LongLink0000000000000000000000000000017300000000000011216 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/defaultcertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/s0000644000076600000240000000504113261244762033671 0ustar bmwstaff00000000000000# You may add here your # server { # ... # } # statements for each of your virtual hosts to this file ## # You should look at the following URL's in order to grasp a solid understanding # of Nginx configuration files in order to fully unleash the power of Nginx. # http://wiki.nginx.org/Pitfalls # http://wiki.nginx.org/QuickStart # http://wiki.nginx.org/Configuration # # Generally, you will want to move this file somewhere, and start with a clean # file but keep this around for reference. Or just disable in sites-enabled. # # Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples. ## server { listen 80 default_server; listen [::]:80 default_server ipv6only=on; root /usr/share/nginx/html; index index.html index.htm; # Make site accessible from http://localhost/ server_name localhost; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; # Uncomment to enable naxsi on this location # include /etc/nginx/naxsi.rules } # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests #location /RequestDenied { # proxy_pass http://127.0.0.1:8080; #} #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # #error_page 500 502 503 504 /50x.html; #location = /50x.html { # root /usr/share/nginx/html; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # fastcgi_split_path_info ^(.+\.php)(/.+)$; # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini # # # With php5-cgi alone: # fastcgi_pass 127.0.0.1:9000; # # With php5-fpm: # fastcgi_pass unix:/var/run/php5-fpm.sock; # fastcgi_index index.php; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } # another virtual host using mix of IP-, name-, and port-based configuration # #server { # listen 8000; # listen somename:8080; # server_name somename alias another.alias; # root html; # index index.html index.htm; # # location / { # try_files $uri $uri/ =404; # } #} # HTTPS server # #server { # listen 443; # server_name localhost; # # root html; # index index.html index.htm; # # ssl on; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # # ssl_session_timeout 5m; # # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; # ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; # ssl_prefer_server_ciphers on; # # location / { # try_files $uri $uri/ =404; # } #} ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-wincertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/k0000644000076600000240000000341513261244762033664 0ustar bmwstaff00000000000000charset_map koi8-r windows-1251 { 80 88; # euro 95 95; # bullet 9A A0; #   9E B7; # · A3 B8; # small yo A4 BA; # small Ukrainian ye A6 B3; # small Ukrainian i A7 BF; # small Ukrainian yi AD B4; # small Ukrainian soft g AE A2; # small Byelorussian short u B0 B0; # ° B3 A8; # capital YO B4 AA; # capital Ukrainian YE B6 B2; # capital Ukrainian I B7 AF; # capital Ukrainian YI B9 B9; # numero sign BD A5; # capital Ukrainian soft G BE A1; # capital Byelorussian short U BF A9; # (C) C0 FE; # small yu C1 E0; # small a C2 E1; # small b C3 F6; # small ts C4 E4; # small d C5 E5; # small ye C6 F4; # small f C7 E3; # small g C8 F5; # small kh C9 E8; # small i CA E9; # small j CB EA; # small k CC EB; # small l CD EC; # small m CE ED; # small n CF EE; # small o D0 EF; # small p D1 FF; # small ya D2 F0; # small r D3 F1; # small s D4 F2; # small t D5 F3; # small u D6 E6; # small zh D7 E2; # small v D8 FC; # small soft sign D9 FB; # small y DA E7; # small z DB F8; # small sh DC FD; # small e DD F9; # small shch DE F7; # small ch DF FA; # small hard sign E0 DE; # capital YU E1 C0; # capital A E2 C1; # capital B E3 D6; # capital TS E4 C4; # capital D E5 C5; # capital YE E6 D4; # capital F E7 C3; # capital G E8 D5; # capital KH E9 C8; # capital I EA C9; # capital J EB CA; # capital K EC CB; # capital L ED CC; # capital M EE CD; # capital N EF CE; # capital O F0 CF; # capital P F1 DF; # capital YA F2 D0; # capital R F3 D1; # capital S F4 D2; # capital T F5 D3; # capital U F6 C6; # capital ZH F7 C2; # capital V F8 DC; # capital soft sign F9 DB; # capital Y FA C7; # capital Z FB D8; # capital SH FC DD; # capital E FD D9; # capital SHCH FE D7; # capital CH FF DA; # capital hard sign } ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.typescertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/m0000644000076600000240000000404513261244762033666 0ustar bmwstaff00000000000000types { text/html html htm shtml; text/css css; text/xml xml rss; image/gif gif; image/jpeg jpeg jpg; application/x-javascript js; application/atom+xml atom; text/mathml mml; text/plain txt; text/vnd.sun.j2me.app-descriptor jad; text/vnd.wap.wml wml; text/x-component htc; image/png png; image/tiff tif tiff; image/vnd.wap.wbmp wbmp; image/x-icon ico; image/x-jng jng; image/x-ms-bmp bmp; image/svg+xml svg svgz; application/java-archive jar war ear; application/json json; application/mac-binhex40 hqx; application/msword doc; application/pdf pdf; application/postscript ps eps ai; application/rtf rtf; application/vnd.ms-excel xls; application/vnd.ms-powerpoint ppt; application/vnd.wap.wmlc wmlc; application/vnd.google-earth.kml+xml kml; application/vnd.google-earth.kmz kmz; application/x-7z-compressed 7z; application/x-cocoa cco; application/x-java-archive-diff jardiff; application/x-java-jnlp-file jnlp; application/x-makeself run; application/x-perl pl pm; application/x-pilot prc pdb; application/x-rar-compressed rar; application/x-redhat-package-manager rpm; application/x-sea sea; application/x-shockwave-flash swf; application/x-stuffit sit; application/x-tcl tcl tk; application/x-x509-ca-cert der pem crt; application/x-xpinstall xpi; application/xhtml+xml xhtml; application/zip zip; application/octet-stream bin exe dll; application/octet-stream deb; application/octet-stream dmg; application/octet-stream eot; application/octet-stream iso img; application/octet-stream msi msp msm; application/ogg ogx; audio/midi mid midi kar; audio/mpeg mpga mpega mp2 mp3 m4a; audio/ogg oga ogg spx; audio/x-realaudio ra; audio/webm weba; video/3gpp 3gpp 3gp; video/mp4 mp4; video/mpeg mpeg mpg mpe; video/ogg ogv; video/quicktime mov; video/webm webm; video/x-flv flv; video/x-mng mng; video/x-ms-asf asx asf; video/x-ms-wmv wmv; video/x-msvideo avi; } ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utfcertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/k0000644000076600000240000000432213261244762033662 0ustar bmwstaff00000000000000# This map is not a full koi8-r <> utf8 map: it does not contain # box-drawing and some other characters. Besides this map contains # several koi8-u and Byelorussian letters which are not in koi8-r. # If you need a full and standard map, use contrib/unicode2nginx/koi-utf # map instead. charset_map koi8-r utf-8 { 80 E282AC; # euro 95 E280A2; # bullet 9A C2A0; #   9E C2B7; # · A3 D191; # small yo A4 D194; # small Ukrainian ye A6 D196; # small Ukrainian i A7 D197; # small Ukrainian yi AD D291; # small Ukrainian soft g AE D19E; # small Byelorussian short u B0 C2B0; # ° B3 D081; # capital YO B4 D084; # capital Ukrainian YE B6 D086; # capital Ukrainian I B7 D087; # capital Ukrainian YI B9 E28496; # numero sign BD D290; # capital Ukrainian soft G BE D18E; # capital Byelorussian short U BF C2A9; # (C) C0 D18E; # small yu C1 D0B0; # small a C2 D0B1; # small b C3 D186; # small ts C4 D0B4; # small d C5 D0B5; # small ye C6 D184; # small f C7 D0B3; # small g C8 D185; # small kh C9 D0B8; # small i CA D0B9; # small j CB D0BA; # small k CC D0BB; # small l CD D0BC; # small m CE D0BD; # small n CF D0BE; # small o D0 D0BF; # small p D1 D18F; # small ya D2 D180; # small r D3 D181; # small s D4 D182; # small t D5 D183; # small u D6 D0B6; # small zh D7 D0B2; # small v D8 D18C; # small soft sign D9 D18B; # small y DA D0B7; # small z DB D188; # small sh DC D18D; # small e DD D189; # small shch DE D187; # small ch DF D18A; # small hard sign E0 D0AE; # capital YU E1 D090; # capital A E2 D091; # capital B E3 D0A6; # capital TS E4 D094; # capital D E5 D095; # capital YE E6 D0A4; # capital F E7 D093; # capital G E8 D0A5; # capital KH E9 D098; # capital I EA D099; # capital J EB D09A; # capital K EC D09B; # capital L ED D09C; # capital M EE D09D; # capital N EF D09E; # capital O F0 D09F; # capital P F1 D0AF; # capital YA F2 D0A0; # capital R F3 D0A1; # capital S F4 D0A2; # capital T F5 D0A3; # capital U F6 D096; # capital ZH F7 D092; # capital V F8 D0AC; # capital soft sign F9 D0AB; # capital Y FA D097; # capital Z FB D0A8; # capital SH FC D0AD; # capital E FD D0A9; # capital SHCH FE D0A7; # capital CH FF D0AA; # capital hard sign } ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utfcertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/w0000644000076600000240000000600013261244762033671 0ustar bmwstaff00000000000000# This map is not a full windows-1251 <> utf8 map: it does not # contain Serbian and Macedonian letters. If you need a full map, # use contrib/unicode2nginx/win-utf map instead. charset_map windows-1251 utf-8 { 82 E2809A; # single low-9 quotation mark 84 E2809E; # double low-9 quotation mark 85 E280A6; # ellipsis 86 E280A0; # dagger 87 E280A1; # double dagger 88 E282AC; # euro 89 E280B0; # per mille 91 E28098; # left single quotation mark 92 E28099; # right single quotation mark 93 E2809C; # left double quotation mark 94 E2809D; # right double quotation mark 95 E280A2; # bullet 96 E28093; # en dash 97 E28094; # em dash 99 E284A2; # trade mark sign A0 C2A0; #   A1 D18E; # capital Byelorussian short U A2 D19E; # small Byelorussian short u A4 C2A4; # currency sign A5 D290; # capital Ukrainian soft G A6 C2A6; # borken bar A7 C2A7; # section sign A8 D081; # capital YO A9 C2A9; # (C) AA D084; # capital Ukrainian YE AB C2AB; # left-pointing double angle quotation mark AC C2AC; # not sign AD C2AD; # soft hyphen AE C2AE; # (R) AF D087; # capital Ukrainian YI B0 C2B0; # ° B1 C2B1; # plus-minus sign B2 D086; # capital Ukrainian I B3 D196; # small Ukrainian i B4 D291; # small Ukrainian soft g B5 C2B5; # micro sign B6 C2B6; # pilcrow sign B7 C2B7; # · B8 D191; # small yo B9 E28496; # numero sign BA D194; # small Ukrainian ye BB C2BB; # right-pointing double angle quotation mark BF D197; # small Ukrainian yi C0 D090; # capital A C1 D091; # capital B C2 D092; # capital V C3 D093; # capital G C4 D094; # capital D C5 D095; # capital YE C6 D096; # capital ZH C7 D097; # capital Z C8 D098; # capital I C9 D099; # capital J CA D09A; # capital K CB D09B; # capital L CC D09C; # capital M CD D09D; # capital N CE D09E; # capital O CF D09F; # capital P D0 D0A0; # capital R D1 D0A1; # capital S D2 D0A2; # capital T D3 D0A3; # capital U D4 D0A4; # capital F D5 D0A5; # capital KH D6 D0A6; # capital TS D7 D0A7; # capital CH D8 D0A8; # capital SH D9 D0A9; # capital SHCH DA D0AA; # capital hard sign DB D0AB; # capital Y DC D0AC; # capital soft sign DD D0AD; # capital E DE D0AE; # capital YU DF D0AF; # capital YA E0 D0B0; # small a E1 D0B1; # small b E2 D0B2; # small v E3 D0B3; # small g E4 D0B4; # small d E5 D0B5; # small ye E6 D0B6; # small zh E7 D0B7; # small z E8 D0B8; # small i E9 D0B9; # small j EA D0BA; # small k EB D0BB; # small l EC D0BC; # small m ED D0BD; # small n EE D0BE; # small o EF D0BF; # small p F0 D180; # small r F1 D181; # small s F2 D182; # small t F3 D183; # small u F4 D184; # small f F5 D185; # small kh F6 D186; # small ts F7 D187; # small ch F8 D188; # small sh F9 D189; # small shch FA D18A; # small hard sign FB D18B; # small y FC D18C; # small soft sign FD D18D; # small e FE D18E; # small yu FF D18F; # small ya } ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.confcertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/n0000644000076600000240000000310113261244762033657 0ustar bmwstaff00000000000000user www-data; worker_processes 4; pid /run/nginx.pid; events { worker_connections 768; # multi_accept on; } http { ## # Basic Settings ## sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; # server_tokens off; # server_names_hash_bucket_size 64; # server_name_in_redirect off; include /etc/nginx/mime.types; default_type application/octet-stream; ## # Logging Settings ## access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; ## # Gzip Settings ## gzip on; gzip_disable "msie6"; # gzip_vary on; # gzip_proxied any; # gzip_comp_level 6; # gzip_buffers 16 8k; # gzip_http_version 1.1; # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; ## # nginx-naxsi config ## # Uncomment it if you installed nginx-naxsi ## #include /etc/nginx/naxsi_core.rules; ## # nginx-passenger config ## # Uncomment it if you installed nginx-passenger ## #passenger_root /usr; #passenger_ruby /usr/bin/ruby; ## # Virtual Host Configs ## include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; } #mail { # # See sample authentication script at: # # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript # # # auth_http localhost/auth.php; # # pop3_capabilities "TOP" "USER"; # # imap_capabilities "IMAP4rev1" "UIDPLUS"; # # server { # listen localhost:110; # protocol pop3; # proxy on; # } # # server { # listen localhost:143; # protocol imap; # proxy on; # } #} ././@LongLink0000000000000000000000000000016400000000000011216 Lustar 00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rulescertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/n0000644000076600000240000001225013261244762033664 0ustar bmwstaff00000000000000################################## ## INTERNAL RULES IDS:1-10 ## ################################## #weird_request : 1 #big_body : 2 #no_content_type : 3 #MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999; ################################## ## SQL Injections IDs:1000-1099 ## ################################## MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1001; MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; ## Hardcore rules MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; MainRule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; MainRule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; MainRule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; ## end of hardcore rules MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; MainRule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4" id:1008; MainRule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; MainRule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1010; MainRule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1011; MainRule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1013; MainRule "str:\"" "msg:double quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1014; MainRule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; ############################### ## OBVIOUS RFI IDs:1100-1199 ## ############################### MainRule "str:http://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; MainRule "str:https://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; MainRule "str:ftp://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; MainRule "str:php://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; ####################################### ## Directory traversal IDs:1200-1299 ## ####################################### MainRule "str:.." "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; MainRule "str:/etc/passwd" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; MainRule "str:c:\\" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; MainRule "str:cmd.exe" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; MainRule "str:\\" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; #MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206; ######################################## ## Cross Site Scripting IDs:1300-1399 ## ######################################## MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; MainRule "str:'" "msg:simple quote" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1306; MainRule "str:\"" "msg:double quote" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1307; MainRule "str:(" "msg:parenthesis" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1308; MainRule "str:)" "msg:parenthesis" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1309; MainRule "str:[" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; MainRule "str:]" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; MainRule "str:~" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; MainRule "str:;" "msg:semi coma" "mz:ARGS|URL|BODY" "s:$XSS:8" id:1313; MainRule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; MainRule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; #################################### ## Evading tricks IDs: 1400-1500 ## #################################### MainRule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; MainRule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither multipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402; ############################# ## File uploads: 1500-1600 ## ############################# MainRule "rx:.ph*|.asp*" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500; certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/broken.conf0000644000076600000240000000017513261244762026227 0ustar bmwstaff00000000000000# A faulty configuration file pid logs/nginx.pid; events { worker_connections 1024; } include foo.conf; @@@ certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/0000755000076600000240000000000013261245052026605 5ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default0000644000076600000240000000032313261244762030161 0ustar bmwstaff00000000000000server { listen myhost default_server; listen otherhost default_server; server_name "www.example.org"; location / { root html; index index.html index.htm; } } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/example.com0000644000076600000240000000020513261244762030744 0ustar bmwstaff00000000000000server { listen 69.50.225.155:9000; listen 127.0.0.1; server_name .example.com; server_name example.*; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/sslon.com0000644000076600000240000000015713261244762030455 0ustar bmwstaff00000000000000server { server_name sslon.com; ssl on; ssl_certificate snakeoil.cert; ssl_certificate_key snakeoil.key; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/migration.com0000644000076600000240000000057713261244762031316 0ustar bmwstaff00000000000000server { server_name migration.com; server_name summer.com; } server { listen 443 ssl; server_name migration.com; server_name geese.com; ssl_certificate cert.pem; ssl_certificate_key cert.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6.com0000644000076600000240000000011013261244762030170 0ustar bmwstaff00000000000000server { listen 80; listen [::]:80; server_name ipv6.com; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com0000644000076600000240000000023413261244762030721 0ustar bmwstaff00000000000000server { listen 443 ssl; listen [::]:443 ssl ipv6only=on; listen 5001 ssl; listen [::]:5001 ssl ipv6only=on; server_name ipv6ssl.com; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/globalssl.com0000644000076600000240000000022013261244762031270 0ustar bmwstaff00000000000000server { server_name globalssl.com; listen 4.8.2.6:57; } server { server_name globalsslsetssl.com; listen 4.8.2.6:57 ssl; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/server.conf0000644000076600000240000000005513261244762026252 0ustar bmwstaff00000000000000server_name somename alias another.alias; certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/edge_cases.conf0000644000076600000240000000110313261244762027021 0ustar bmwstaff00000000000000# This is not a valid nginx config file but it tests edge cases in valid nginx syntax server { server_name simple; } server { server_name with.if; location ~ ^/services/.+$ { if ($request_filename ~* \.(ttf|woff)$) { add_header Access-Control-Allow-Origin "*"; } } } server { server_name with.complicated.headers; location ~* \.(?:gif|jpe?g|png)$ { add_header Pragma public; add_header Cache-Control 'public, must-revalidate, proxy-revalidate' "test,;{}" foo; blah "hello;world"; try_files $uri @rewrites; } } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf0000644000076600000240000000030513261244762031331 0ustar bmwstaff00000000000000# Use bar.conf when it's a full moon! include foo.conf; # Kilroy was here check_status; server { # # Don't forget to open up your firewall! # listen 1234; # listen 80; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/multiline_quotes.conf0000644000076600000240000000111113261244762030340 0ustar bmwstaff00000000000000# Test nginx configuration file with multiline quoted strings. # Good example of usage for multilined quoted values is when # using Openresty's Lua directives and you wish to keep the # inline Lua code readable. http { server { listen *:443; # because there should be no other port open. location / { body_filter_by_lua 'ngx.ctx.buffered = (ngx.ctx.buffered or "") .. string.sub(ngx.arg[1], 1, 1000) if ngx.arg[2] then ngx.var.resp_body = ngx.ctx.buffered end'; } } } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/mime.types0000644000076600000240000000000013261244762026100 0ustar bmwstaff00000000000000certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/nginx.conf0000644000076600000240000000517413261244762026076 0ustar bmwstaff00000000000000# standard default nginx config user nobody; worker_processes 1; error_log logs/error.log; error_log logs/error.log notice; error_log logs/error.log info; pid logs/nginx.pid; events { worker_connections 1024; } empty { } include foo.conf; http { include mime.types; include sites-enabled/*; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; tcp_nopush on; keepalive_timeout 0; gzip on; server { listen 8080; server_name localhost; server_name ~^(www\.)?(example|bar)\.; charset koi8-r; access_log logs/host.access.log main; location / { root html; index index.html index.htm; } error_page 404 /404.html; # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # proxy the PHP scripts to Nginx listening on 127.0.0.1:80 # location ~ \.php$ { proxy_pass http://127.0.0.1; } # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; } # deny access to .htaccess files, if Nginx's document root # concurs with nginx's one # location ~ /\.ht { deny all; } } # another virtual host using mix of IP-, name-, and port-based configuration # server { listen 8000; listen somename:8080; include server.conf; location / { root html; index index.html index.htm; } } # HTTPS server # #server { # listen 443 ssl; # server_name localhost; # ssl_certificate cert.pem; # ssl_certificate_key cert.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # location / { # root html; # index index.html index.htm; # } #} #include conf.d/test.conf; } certbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/comment_in_file.conf0000644000076600000240000000003113261244762030065 0ustar bmwstaff00000000000000# a comment inside a filecertbot-nginx-0.23.0/certbot_nginx/tests/testdata/etc_nginx/foo.conf0000644000076600000240000000103413261244762025525 0ustar bmwstaff00000000000000# a test nginx conf user www-data; http { server { listen *:80 default_server ssl; server_name *.www.foo.com *.www.example.com; root /home/ubuntu/sites/foo/; location /status { types { image/jpeg jpg; } } location ~ case_sensitive\.php$ { index index.php; root /var/root; } location ~* case_insensitive\.php$ {} location = exact_match\.php$ {} location ^~ ignore_regex\.php$ {} } } certbot-nginx-0.23.0/certbot_nginx/tests/obj_test.py0000644000076600000240000002305613261244762022477 0ustar bmwstaff00000000000000"""Test the helper objects in certbot_nginx.obj.""" import unittest import itertools class AddrTest(unittest.TestCase): """Test the Addr class.""" def setUp(self): from certbot_nginx.obj import Addr self.addr1 = Addr.fromstring("192.168.1.1") self.addr2 = Addr.fromstring("192.168.1.1:* ssl") self.addr3 = Addr.fromstring("192.168.1.1:80") self.addr4 = Addr.fromstring("*:80 default_server ssl") self.addr5 = Addr.fromstring("myhost") self.addr6 = Addr.fromstring("80 default_server spdy") self.addr7 = Addr.fromstring("unix:/var/run/nginx.sock") self.addr8 = Addr.fromstring("*:80 default ssl") def test_fromstring(self): self.assertEqual(self.addr1.get_addr(), "192.168.1.1") self.assertEqual(self.addr1.get_port(), "") self.assertFalse(self.addr1.ssl) self.assertFalse(self.addr1.default) self.assertEqual(self.addr2.get_addr(), "192.168.1.1") self.assertEqual(self.addr2.get_port(), "*") self.assertTrue(self.addr2.ssl) self.assertFalse(self.addr2.default) self.assertEqual(self.addr3.get_addr(), "192.168.1.1") self.assertEqual(self.addr3.get_port(), "80") self.assertFalse(self.addr3.ssl) self.assertFalse(self.addr3.default) self.assertEqual(self.addr4.get_addr(), "*") self.assertEqual(self.addr4.get_port(), "80") self.assertTrue(self.addr4.ssl) self.assertTrue(self.addr4.default) self.assertEqual(self.addr5.get_addr(), "myhost") self.assertEqual(self.addr5.get_port(), "") self.assertFalse(self.addr5.ssl) self.assertFalse(self.addr5.default) self.assertEqual(self.addr6.get_addr(), "") self.assertEqual(self.addr6.get_port(), "80") self.assertFalse(self.addr6.ssl) self.assertTrue(self.addr6.default) self.assertTrue(self.addr8.default) self.assertEqual(None, self.addr7) def test_str(self): self.assertEqual(str(self.addr1), "192.168.1.1") self.assertEqual(str(self.addr2), "192.168.1.1:* ssl") self.assertEqual(str(self.addr3), "192.168.1.1:80") self.assertEqual(str(self.addr4), "*:80 default_server ssl") self.assertEqual(str(self.addr5), "myhost") self.assertEqual(str(self.addr6), "80 default_server") self.assertEqual(str(self.addr8), "*:80 default_server ssl") def test_to_string(self): self.assertEqual(self.addr1.to_string(), "192.168.1.1") self.assertEqual(self.addr2.to_string(), "192.168.1.1:* ssl") self.assertEqual(self.addr3.to_string(), "192.168.1.1:80") self.assertEqual(self.addr4.to_string(), "*:80 default_server ssl") self.assertEqual(self.addr4.to_string(include_default=False), "*:80 ssl") self.assertEqual(self.addr5.to_string(), "myhost") self.assertEqual(self.addr6.to_string(), "80 default_server") self.assertEqual(self.addr6.to_string(include_default=False), "80") def test_eq(self): from certbot_nginx.obj import Addr new_addr1 = Addr.fromstring("192.168.1.1 spdy") self.assertEqual(self.addr1, new_addr1) self.assertNotEqual(self.addr1, self.addr2) self.assertFalse(self.addr1 == 3333) def test_equivalent_any_addresses(self): from certbot_nginx.obj import Addr any_addresses = ("0.0.0.0:80 default_server ssl", "80 default_server ssl", "*:80 default_server ssl", "80 default ssl") for first, second in itertools.combinations(any_addresses, 2): self.assertEqual(Addr.fromstring(first), Addr.fromstring(second)) # Also, make sure ports are checked. self.assertNotEqual(Addr.fromstring(any_addresses[0]), Addr.fromstring("0.0.0.0:443 default_server ssl")) # And they aren't equivalent to a specified address. for any_address in any_addresses: self.assertNotEqual( Addr.fromstring("192.168.1.2:80 default_server ssl"), Addr.fromstring(any_address)) def test_set_inclusion(self): from certbot_nginx.obj import Addr set_a = set([self.addr1, self.addr2]) addr1b = Addr.fromstring("192.168.1.1") addr2b = Addr.fromstring("192.168.1.1:* ssl") set_b = set([addr1b, addr2b]) self.assertEqual(set_a, set_b) class VirtualHostTest(unittest.TestCase): """Test the VirtualHost class.""" def setUp(self): from certbot_nginx.obj import VirtualHost from certbot_nginx.obj import Addr raw1 = [ ['listen', '69.50.225.155:9000'], [['if', '($scheme', '!=', '"https") '], [['return', '301', 'https://$host$request_uri']] ], ['#', ' managed by Certbot'] ] self.vhost1 = VirtualHost( "filep", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), raw1, []) raw2 = [ ['listen', '69.50.225.155:9000'], [['if', '($scheme', '!=', '"https") '], [['return', '301', 'https://$host$request_uri']] ] ] self.vhost2 = VirtualHost( "filep", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), raw2, []) raw3 = [ ['listen', '69.50.225.155:9000'], ['rewrite', '^(.*)$', '$scheme://www.domain.com$1', 'permanent'] ] self.vhost3 = VirtualHost( "filep", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), raw3, []) raw4 = [ ['listen', '69.50.225.155:9000'], ['server_name', 'return.com'] ] self.vhost4 = VirtualHost( "filp", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), raw4, []) raw_has_hsts = [ ['listen', '69.50.225.155:9000'], ['server_name', 'return.com'], ['add_header', 'always', 'set', 'Strict-Transport-Security', '\"max-age=31536000\"'], ] self.vhost_has_hsts = VirtualHost( "filep", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), raw_has_hsts, []) def test_eq(self): from certbot_nginx.obj import Addr from certbot_nginx.obj import VirtualHost vhost1b = VirtualHost( "filep", set([Addr.fromstring("localhost blah")]), False, False, set(['localhost']), [], []) self.assertEqual(vhost1b, self.vhost1) self.assertEqual(str(vhost1b), str(self.vhost1)) self.assertFalse(vhost1b == 1234) def test_str(self): stringified = '\n'.join(['file: filep', 'addrs: localhost', "names: ['localhost']", 'ssl: False', 'enabled: False']) self.assertEqual(stringified, str(self.vhost1)) def test_has_header(self): self.assertTrue(self.vhost_has_hsts.has_header('Strict-Transport-Security')) self.assertFalse(self.vhost_has_hsts.has_header('Bogus-Header')) self.assertFalse(self.vhost1.has_header('Strict-Transport-Security')) self.assertFalse(self.vhost1.has_header('Bogus-Header')) def test_contains_list(self): from certbot_nginx.obj import VirtualHost from certbot_nginx.obj import Addr from certbot_nginx.configurator import _test_block_from_block test_block = [ ['\n ', 'return', ' ', '301', ' ', 'https://$host$request_uri'], ['\n'] ] test_needle = _test_block_from_block(test_block) test_haystack = [['listen', '80'], ['root', '/var/www/html'], ['index', 'index.html index.htm index.nginx-debian.html'], ['server_name', 'two.functorkitten.xyz'], ['listen', '443 ssl'], ['#', ' managed by Certbot'], ['ssl_certificate', '/etc/letsencrypt/live/two.functorkitten.xyz/fullchain.pem'], ['#', ' managed by Certbot'], ['ssl_certificate_key', '/etc/letsencrypt/live/two.functorkitten.xyz/privkey.pem'], ['#', ' managed by Certbot'], ['return', '301', 'https://$host$request_uri'], ['#', ' managed by Certbot'], []] vhost_haystack = VirtualHost( "filp", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), test_haystack, []) test_bad_haystack = [['listen', '80'], ['root', '/var/www/html'], ['index', 'index.html index.htm index.nginx-debian.html'], ['server_name', 'two.functorkitten.xyz'], ['listen', '443 ssl'], ['#', ' managed by Certbot'], ['ssl_certificate', '/etc/letsencrypt/live/two.functorkitten.xyz/fullchain.pem'], ['#', ' managed by Certbot'], ['ssl_certificate_key', '/etc/letsencrypt/live/two.functorkitten.xyz/privkey.pem'], ['#', ' managed by Certbot'], [['if', '($scheme', '!=', '"https")'], [['return', '302', 'https://$host$request_uri']] ], ['#', ' managed by Certbot'], []] vhost_bad_haystack = VirtualHost( "filp", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), test_bad_haystack, []) self.assertTrue(vhost_haystack.contains_list(test_needle)) self.assertFalse(vhost_bad_haystack.contains_list(test_needle)) if __name__ == "__main__": unittest.main() # pragma: no cover certbot-nginx-0.23.0/certbot_nginx/tests/configurator_test.py0000644000076600000240000012347113261244762024431 0ustar bmwstaff00000000000000# pylint: disable=too-many-public-methods """Test for certbot_nginx.configurator.""" import os import shutil import unittest import mock import OpenSSL from acme import challenges from acme import messages from certbot import achallenges from certbot import crypto_util from certbot import errors from certbot.tests import util as certbot_test_util from certbot_nginx import constants from certbot_nginx import obj from certbot_nginx import parser from certbot_nginx.configurator import _redirect_block_for_domain from certbot_nginx.nginxparser import UnspacedList from certbot_nginx.tests import util class NginxConfiguratorTest(util.NginxTest): """Test a semi complex vhost configuration.""" def setUp(self): super(NginxConfiguratorTest, self).setUp() self.config = util.get_nginx_configurator( self.config_path, self.config_dir, self.work_dir, self.logs_dir) def tearDown(self): shutil.rmtree(self.temp_dir) shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) shutil.rmtree(self.logs_dir) @mock.patch("certbot_nginx.configurator.util.exe_exists") def test_prepare_no_install(self, mock_exe_exists): mock_exe_exists.return_value = False self.assertRaises( errors.NoInstallationError, self.config.prepare) def test_prepare(self): self.assertEqual((1, 6, 2), self.config.version) self.assertEqual(10, len(self.config.parser.parsed)) @mock.patch("certbot_nginx.configurator.util.exe_exists") @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_prepare_initializes_version(self, mock_popen, mock_exe_exists): mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.6.2", "built by clang 6.0 (clang-600.0.56)" " (based on LLVM 3.5svn)", "TLS SNI support enabled", "configure arguments: --prefix=/usr/local/Cellar/" "nginx/1.6.2 --with-http_ssl_module"])) mock_exe_exists.return_value = True self.config.version = None self.config.config_test = mock.Mock() self.config.prepare() self.assertEqual((1, 6, 2), self.config.version) def test_prepare_locked(self): server_root = self.config.conf("server-root") self.config.config_test = mock.Mock() os.remove(os.path.join(server_root, ".certbot.lock")) certbot_test_util.lock_and_call(self._test_prepare_locked, server_root) @mock.patch("certbot_nginx.configurator.util.exe_exists") def _test_prepare_locked(self, unused_exe_exists): try: self.config.prepare() except errors.PluginError as err: err_msg = str(err) self.assertTrue("lock" in err_msg) self.assertTrue(self.config.conf("server-root") in err_msg) else: # pragma: no cover self.fail("Exception wasn't raised!") @mock.patch("certbot_nginx.configurator.socket.gethostbyaddr") def test_get_all_names(self, mock_gethostbyaddr): mock_gethostbyaddr.return_value = ('155.225.50.69.nephoscale.net', [], []) names = self.config.get_all_names() self.assertEqual(names, set( ["155.225.50.69.nephoscale.net", "www.example.org", "another.alias", "migration.com", "summer.com", "geese.com", "sslon.com", "globalssl.com", "globalsslsetssl.com", "ipv6.com", "ipv6ssl.com"])) def test_supported_enhancements(self): self.assertEqual(['redirect', 'ensure-http-header', 'staple-ocsp'], self.config.supported_enhancements()) def test_enhance(self): self.assertRaises( errors.PluginError, self.config.enhance, 'myhost', 'unknown_enhancement') def test_get_chall_pref(self): self.assertEqual([challenges.TLSSNI01, challenges.HTTP01], self.config.get_chall_pref('myhost')) def test_save(self): filep = self.config.parser.abs_path('sites-enabled/example.com') mock_vhost = obj.VirtualHost(filep, None, None, None, set(['.example.com', 'example.*']), None, [0]) self.config.parser.add_server_directives( mock_vhost, [['listen', ' ', '5001', ' ', 'ssl']]) self.config.save() # pylint: disable=protected-access parsed = self.config.parser._parse_files(filep, override=True) self.assertEqual([[['server'], [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], ['listen', '5001', 'ssl'], ['#', parser.COMMENT]]]], parsed[0]) def test_choose_vhosts(self): localhost_conf = set(['localhost', r'~^(www\.)?(example|bar)\.']) server_conf = set(['somename', 'another.alias', 'alias']) example_conf = set(['.example.com', 'example.*']) foo_conf = set(['*.www.foo.com', '*.www.example.com']) ipv6_conf = set(['ipv6.com']) results = {'localhost': localhost_conf, 'alias': server_conf, 'example.com': example_conf, 'example.com.uk.test': example_conf, 'www.example.com': example_conf, 'test.www.example.com': foo_conf, 'abc.www.foo.com': foo_conf, 'www.bar.co.uk': localhost_conf, 'ipv6.com': ipv6_conf} conf_path = {'localhost': "etc_nginx/nginx.conf", 'alias': "etc_nginx/nginx.conf", 'example.com': "etc_nginx/sites-enabled/example.com", 'example.com.uk.test': "etc_nginx/sites-enabled/example.com", 'www.example.com': "etc_nginx/sites-enabled/example.com", 'test.www.example.com': "etc_nginx/foo.conf", 'abc.www.foo.com': "etc_nginx/foo.conf", 'www.bar.co.uk': "etc_nginx/nginx.conf", 'ipv6.com': "etc_nginx/sites-enabled/ipv6.com"} bad_results = ['www.foo.com', 'example', 't.www.bar.co', '69.255.225.155'] for name in results: vhost = self.config.choose_vhosts(name)[0] path = os.path.relpath(vhost.filep, self.temp_dir) self.assertEqual(results[name], vhost.names) self.assertEqual(conf_path[name], path) # IPv6 specific checks if name == "ipv6.com": self.assertTrue(vhost.ipv6_enabled()) # Make sure that we have SSL enabled also for IPv6 addr self.assertTrue( any([True for x in vhost.addrs if x.ssl and x.ipv6])) for name in bad_results: self.assertRaises(errors.MisconfigurationError, self.config.choose_vhosts, name) def test_ipv6only(self): # ipv6_info: (ipv6_active, ipv6only_present) self.assertEquals((True, False), self.config.ipv6_info("80")) # Port 443 has ipv6only=on because of ipv6ssl.com vhost self.assertEquals((True, True), self.config.ipv6_info("443")) def test_ipv6only_detection(self): self.config.version = (1, 3, 1) self.config.deploy_cert( "ipv6.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") for addr in self.config.choose_vhosts("ipv6.com")[0].addrs: self.assertFalse(addr.ipv6only) def test_more_info(self): self.assertTrue('nginx.conf' in self.config.more_info()) def test_deploy_cert_requires_fullchain_path(self): self.config.version = (1, 3, 1) self.assertRaises(errors.PluginError, self.config.deploy_cert, "www.example.com", "example/cert.pem", "example/key.pem", "example/chain.pem", None) @mock.patch('certbot_nginx.parser.NginxParser.update_or_add_server_directives') def test_deploy_cert_raise_on_add_error(self, mock_update_or_add_server_directives): mock_update_or_add_server_directives.side_effect = errors.MisconfigurationError() self.assertRaises( errors.PluginError, self.config.deploy_cert, "migration.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") def test_deploy_cert(self): server_conf = self.config.parser.abs_path('server.conf') nginx_conf = self.config.parser.abs_path('nginx.conf') example_conf = self.config.parser.abs_path('sites-enabled/example.com') self.config.version = (1, 3, 1) # Get the default SSL vhost self.config.deploy_cert( "www.example.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.deploy_cert( "another.alias", "/etc/nginx/cert.pem", "/etc/nginx/key.pem", "/etc/nginx/chain.pem", "/etc/nginx/fullchain.pem") self.config.save() self.config.parser.load() parsed_example_conf = util.filter_comments(self.config.parser.parsed[example_conf]) parsed_server_conf = util.filter_comments(self.config.parser.parsed[server_conf]) parsed_nginx_conf = util.filter_comments(self.config.parser.parsed[nginx_conf]) self.assertEqual([[['server'], [ ['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], ['listen', '5001', 'ssl'], ['ssl_certificate', 'example/fullchain.pem'], ['ssl_certificate_key', 'example/key.pem'], ['include', self.config.mod_ssl_conf], ['ssl_dhparam', self.config.ssl_dhparams], ]]], parsed_example_conf) self.assertEqual([['server_name', 'somename', 'alias', 'another.alias']], parsed_server_conf) self.assertTrue(util.contains_at_depth( parsed_nginx_conf, [['server'], [ ['listen', '8000'], ['listen', 'somename:8080'], ['include', 'server.conf'], [['location', '/'], [['root', 'html'], ['index', 'index.html', 'index.htm']]], ['listen', '5001', 'ssl'], ['ssl_certificate', '/etc/nginx/fullchain.pem'], ['ssl_certificate_key', '/etc/nginx/key.pem'], ['include', self.config.mod_ssl_conf], ['ssl_dhparam', self.config.ssl_dhparams], ]], 2)) def test_deploy_cert_add_explicit_listen(self): migration_conf = self.config.parser.abs_path('sites-enabled/migration.com') self.config.deploy_cert( "summer.com", "summer/cert.pem", "summer/key.pem", "summer/chain.pem", "summer/fullchain.pem") self.config.save() self.config.parser.load() parsed_migration_conf = util.filter_comments(self.config.parser.parsed[migration_conf]) self.assertEqual([['server'], [ ['server_name', 'migration.com'], ['server_name', 'summer.com'], ['listen', '80'], ['listen', '5001', 'ssl'], ['ssl_certificate', 'summer/fullchain.pem'], ['ssl_certificate_key', 'summer/key.pem'], ['include', self.config.mod_ssl_conf], ['ssl_dhparam', self.config.ssl_dhparams], ]], parsed_migration_conf[0]) @mock.patch("certbot_nginx.configurator.tls_sni_01.NginxTlsSni01.perform") @mock.patch("certbot_nginx.configurator.http_01.NginxHttp01.perform") @mock.patch("certbot_nginx.configurator.NginxConfigurator.restart") @mock.patch("certbot_nginx.configurator.NginxConfigurator.revert_challenge_config") def test_perform_and_cleanup(self, mock_revert, mock_restart, mock_http_perform, mock_tls_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded achall1 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=messages.ChallengeBody( chall=challenges.TLSSNI01(token=b"kNdwjwOeX0I_A8DXt9Msmg"), uri="https://ca.org/chall0_uri", status=messages.Status("pending"), ), domain="localhost", account_key=self.rsa512jwk) achall2 = achallenges.KeyAuthorizationAnnotatedChallenge( challb=messages.ChallengeBody( chall=challenges.HTTP01(token=b"m8TdO1qik4JVFtgPPurJmg"), uri="https://ca.org/chall1_uri", status=messages.Status("pending"), ), domain="example.com", account_key=self.rsa512jwk) expected = [ achall1.response(self.rsa512jwk), achall2.response(self.rsa512jwk), ] mock_tls_perform.return_value = expected[:1] mock_http_perform.return_value = expected[1:] responses = self.config.perform([achall1, achall2]) self.assertEqual(mock_tls_perform.call_count, 1) self.assertEqual(mock_http_perform.call_count, 1) self.assertEqual(responses, expected) self.config.cleanup([achall1, achall2]) self.assertEqual(0, self.config._chall_out) # pylint: disable=protected-access self.assertEqual(mock_revert.call_count, 1) self.assertEqual(mock_restart.call_count, 2) @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_get_version(self, mock_popen): mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.4.2", "built by clang 6.0 (clang-600.0.56)" " (based on LLVM 3.5svn)", "TLS SNI support enabled", "configure arguments: --prefix=/usr/local/Cellar/" "nginx/1.6.2 --with-http_ssl_module"])) self.assertEqual(self.config.get_version(), (1, 4, 2)) mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/0.9", "built by clang 6.0 (clang-600.0.56)" " (based on LLVM 3.5svn)", "TLS SNI support enabled", "configure arguments: --with-http_ssl_module"])) self.assertEqual(self.config.get_version(), (0, 9)) mock_popen().communicate.return_value = ( "", "\n".join(["blah 0.0.1", "built by clang 6.0 (clang-600.0.56)" " (based on LLVM 3.5svn)", "TLS SNI support enabled", "configure arguments: --with-http_ssl_module"])) self.assertRaises(errors.PluginError, self.config.get_version) mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.4.2", "TLS SNI support enabled"])) self.assertRaises(errors.PluginError, self.config.get_version) mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.4.2", "built by clang 6.0 (clang-600.0.56)" " (based on LLVM 3.5svn)", "configure arguments: --with-http_ssl_module"])) self.assertRaises(errors.PluginError, self.config.get_version) mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/0.8.1", "built by clang 6.0 (clang-600.0.56)" " (based on LLVM 3.5svn)", "TLS SNI support enabled", "configure arguments: --with-http_ssl_module"])) self.assertRaises(errors.NotSupportedError, self.config.get_version) mock_popen.side_effect = OSError("Can't find program") self.assertRaises(errors.PluginError, self.config.get_version) @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_nginx_restart(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 0 self.config.restart() @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_nginx_restart_fail(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 1 self.assertRaises(errors.MisconfigurationError, self.config.restart) @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_no_nginx_start(self, mock_popen): mock_popen.side_effect = OSError("Can't find program") self.assertRaises(errors.MisconfigurationError, self.config.restart) @mock.patch("certbot.util.run_script") def test_config_test_bad_process(self, mock_run_script): mock_run_script.side_effect = errors.SubprocessError self.assertRaises(errors.MisconfigurationError, self.config.config_test) @mock.patch("certbot.util.run_script") def test_config_test(self, _): self.config.config_test() @mock.patch("certbot.reverter.Reverter.recovery_routine") def test_recovery_routine_throws_error_from_reverter(self, mock_recovery_routine): mock_recovery_routine.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.recovery_routine) @mock.patch("certbot.reverter.Reverter.view_config_changes") def test_view_config_changes_throws_error_from_reverter(self, mock_view_config_changes): mock_view_config_changes.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.view_config_changes) @mock.patch("certbot.reverter.Reverter.rollback_checkpoints") def test_rollback_checkpoints_throws_error_from_reverter(self, mock_rollback_checkpoints): mock_rollback_checkpoints.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.rollback_checkpoints) @mock.patch("certbot.reverter.Reverter.revert_temporary_config") def test_revert_challenge_config_throws_error_from_reverter(self, mock_revert_temporary_config): mock_revert_temporary_config.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.revert_challenge_config) @mock.patch("certbot.reverter.Reverter.add_to_checkpoint") def test_save_throws_error_from_reverter(self, mock_add_to_checkpoint): mock_add_to_checkpoint.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.save) def test_get_snakeoil_paths(self): # pylint: disable=protected-access cert, key = self.config._get_snakeoil_paths() self.assertTrue(os.path.exists(cert)) self.assertTrue(os.path.exists(key)) with open(cert) as cert_file: OpenSSL.crypto.load_certificate( OpenSSL.crypto.FILETYPE_PEM, cert_file.read()) with open(key) as key_file: OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, key_file.read()) def test_redirect_enhance(self): # Test that we successfully add a redirect when there is # a listen directive expected = UnspacedList(_redirect_block_for_domain("www.example.com"))[0] example_conf = self.config.parser.abs_path('sites-enabled/example.com') self.config.enhance("www.example.com", "redirect") generated_conf = self.config.parser.parsed[example_conf] self.assertTrue(util.contains_at_depth(generated_conf, expected, 2)) # Test that we successfully add a redirect when there is # no listen directive migration_conf = self.config.parser.abs_path('sites-enabled/migration.com') self.config.enhance("migration.com", "redirect") expected = UnspacedList(_redirect_block_for_domain("migration.com"))[0] generated_conf = self.config.parser.parsed[migration_conf] self.assertTrue(util.contains_at_depth(generated_conf, expected, 2)) def test_split_for_redirect(self): example_conf = self.config.parser.abs_path('sites-enabled/example.com') self.config.deploy_cert( "example.org", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.enhance("www.example.com", "redirect") generated_conf = self.config.parser.parsed[example_conf] self.assertEqual( [[['server'], [ ['server_name', '.example.com'], ['server_name', 'example.*'], [], ['listen', '5001', 'ssl'], ['#', ' managed by Certbot'], ['ssl_certificate', 'example/fullchain.pem'], ['#', ' managed by Certbot'], ['ssl_certificate_key', 'example/key.pem'], ['#', ' managed by Certbot'], ['include', self.config.mod_ssl_conf], ['#', ' managed by Certbot'], ['ssl_dhparam', self.config.ssl_dhparams], ['#', ' managed by Certbot'], [], []]], [['server'], [ [['if', '($host', '=', 'www.example.com)'], [ ['return', '301', 'https://$host$request_uri']]], ['#', ' managed by Certbot'], [], ['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], ['return', '404'], ['#', ' managed by Certbot'], [], [], []]]], generated_conf) def test_split_for_headers(self): example_conf = self.config.parser.abs_path('sites-enabled/example.com') self.config.deploy_cert( "example.org", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.enhance("www.example.com", "ensure-http-header", "Strict-Transport-Security") generated_conf = self.config.parser.parsed[example_conf] self.assertEqual( [[['server'], [ ['server_name', '.example.com'], ['server_name', 'example.*'], [], ['listen', '5001', 'ssl'], ['#', ' managed by Certbot'], ['ssl_certificate', 'example/fullchain.pem'], ['#', ' managed by Certbot'], ['ssl_certificate_key', 'example/key.pem'], ['#', ' managed by Certbot'], ['include', self.config.mod_ssl_conf], ['#', ' managed by Certbot'], ['ssl_dhparam', self.config.ssl_dhparams], ['#', ' managed by Certbot'], [], [], ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always'], ['#', ' managed by Certbot'], [], []]], [['server'], [ ['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], [], [], []]]], generated_conf) def test_http_header_hsts(self): example_conf = self.config.parser.abs_path('sites-enabled/example.com') self.config.enhance("www.example.com", "ensure-http-header", "Strict-Transport-Security") expected = ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always'] generated_conf = self.config.parser.parsed[example_conf] self.assertTrue(util.contains_at_depth(generated_conf, expected, 2)) def test_http_header_hsts_twice(self): self.config.enhance("www.example.com", "ensure-http-header", "Strict-Transport-Security") self.assertRaises( errors.PluginEnhancementAlreadyPresent, self.config.enhance, "www.example.com", "ensure-http-header", "Strict-Transport-Security") @mock.patch('certbot_nginx.obj.VirtualHost.contains_list') def test_certbot_redirect_exists(self, mock_contains_list): # Test that we add no redirect statement if there is already a # redirect in the block that is managed by certbot # Has a certbot redirect mock_contains_list.return_value = True with mock.patch("certbot_nginx.configurator.logger") as mock_logger: self.config.enhance("www.example.com", "redirect") self.assertEqual(mock_logger.info.call_args[0][0], "Traffic on port %s already redirecting to ssl in %s") def test_redirect_dont_enhance(self): # Test that we don't accidentally add redirect to ssl-only block with mock.patch("certbot_nginx.configurator.logger") as mock_logger: self.config.enhance("geese.com", "redirect") self.assertEqual(mock_logger.info.call_args[0][0], 'No matching insecure server blocks listening on port %s found.') def test_double_redirect(self): # Test that we add one redirect for each domain example_conf = self.config.parser.abs_path('sites-enabled/example.com') self.config.enhance("example.com", "redirect") self.config.enhance("example.org", "redirect") expected1 = UnspacedList(_redirect_block_for_domain("example.com"))[0] expected2 = UnspacedList(_redirect_block_for_domain("example.org"))[0] generated_conf = self.config.parser.parsed[example_conf] self.assertTrue(util.contains_at_depth(generated_conf, expected1, 2)) self.assertTrue(util.contains_at_depth(generated_conf, expected2, 2)) def test_staple_ocsp_bad_version(self): self.config.version = (1, 3, 1) self.assertRaises(errors.PluginError, self.config.enhance, "www.example.com", "staple-ocsp", "chain_path") def test_staple_ocsp_no_chain_path(self): self.assertRaises(errors.PluginError, self.config.enhance, "www.example.com", "staple-ocsp", None) def test_staple_ocsp_internal_error(self): self.config.enhance("www.example.com", "staple-ocsp", "chain_path") # error is raised because the server block has conflicting directives self.assertRaises(errors.PluginError, self.config.enhance, "www.example.com", "staple-ocsp", "different_path") def test_staple_ocsp(self): chain_path = "example/chain.pem" self.config.enhance("www.example.com", "staple-ocsp", chain_path) example_conf = self.config.parser.abs_path('sites-enabled/example.com') generated_conf = self.config.parser.parsed[example_conf] self.assertTrue(util.contains_at_depth( generated_conf, ['ssl_trusted_certificate', 'example/chain.pem'], 2)) self.assertTrue(util.contains_at_depth( generated_conf, ['ssl_stapling', 'on'], 2)) self.assertTrue(util.contains_at_depth( generated_conf, ['ssl_stapling_verify', 'on'], 2)) def test_deploy_no_match_default_set(self): default_conf = self.config.parser.abs_path('sites-enabled/default') foo_conf = self.config.parser.abs_path('foo.conf') del self.config.parser.parsed[foo_conf][2][1][0][1][0] # remove default_server self.config.version = (1, 3, 1) self.config.deploy_cert( "www.nomatch.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.save() self.config.parser.load() parsed_default_conf = util.filter_comments(self.config.parser.parsed[default_conf]) self.assertEqual([[['server'], [['listen', 'myhost', 'default_server'], ['listen', 'otherhost', 'default_server'], ['server_name', '"www.example.org"'], [['location', '/'], [['root', 'html'], ['index', 'index.html', 'index.htm']]]]], [['server'], [['listen', 'myhost'], ['listen', 'otherhost'], ['server_name', 'www.nomatch.com'], [['location', '/'], [['root', 'html'], ['index', 'index.html', 'index.htm']]], ['listen', '5001', 'ssl'], ['ssl_certificate', 'example/fullchain.pem'], ['ssl_certificate_key', 'example/key.pem'], ['include', self.config.mod_ssl_conf], ['ssl_dhparam', self.config.ssl_dhparams]]]], parsed_default_conf) self.config.deploy_cert( "nomatch.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.save() self.config.parser.load() parsed_default_conf = util.filter_comments(self.config.parser.parsed[default_conf]) self.assertTrue(util.contains_at_depth(parsed_default_conf, "nomatch.com", 3)) def test_deploy_no_match_default_set_multi_level_path(self): default_conf = self.config.parser.abs_path('sites-enabled/default') foo_conf = self.config.parser.abs_path('foo.conf') del self.config.parser.parsed[default_conf][0][1][0] del self.config.parser.parsed[default_conf][0][1][0] self.config.version = (1, 3, 1) self.config.deploy_cert( "www.nomatch.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.save() self.config.parser.load() parsed_foo_conf = util.filter_comments(self.config.parser.parsed[foo_conf]) self.assertEqual([['server'], [['listen', '*:80', 'ssl'], ['server_name', 'www.nomatch.com'], ['root', '/home/ubuntu/sites/foo/'], [['location', '/status'], [[['types'], [['image/jpeg', 'jpg']]]]], [['location', '~', 'case_sensitive\\.php$'], [['index', 'index.php'], ['root', '/var/root']]], [['location', '~*', 'case_insensitive\\.php$'], []], [['location', '=', 'exact_match\\.php$'], []], [['location', '^~', 'ignore_regex\\.php$'], []], ['ssl_certificate', 'example/fullchain.pem'], ['ssl_certificate_key', 'example/key.pem']]], parsed_foo_conf[1][1][1]) def test_deploy_no_match_no_default_set(self): default_conf = self.config.parser.abs_path('sites-enabled/default') foo_conf = self.config.parser.abs_path('foo.conf') del self.config.parser.parsed[default_conf][0][1][0] del self.config.parser.parsed[default_conf][0][1][0] del self.config.parser.parsed[foo_conf][2][1][0][1][0] self.config.version = (1, 3, 1) self.assertRaises(errors.MisconfigurationError, self.config.deploy_cert, "www.nomatch.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") def test_deploy_no_match_fail_multiple_defaults(self): self.config.version = (1, 3, 1) self.assertRaises(errors.MisconfigurationError, self.config.deploy_cert, "www.nomatch.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") def test_deploy_no_match_add_redirect(self): default_conf = self.config.parser.abs_path('sites-enabled/default') foo_conf = self.config.parser.abs_path('foo.conf') del self.config.parser.parsed[foo_conf][2][1][0][1][0] # remove default_server self.config.version = (1, 3, 1) self.config.deploy_cert( "www.nomatch.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.deploy_cert( "nomatch.com", "example/cert.pem", "example/key.pem", "example/chain.pem", "example/fullchain.pem") self.config.enhance("www.nomatch.com", "redirect") self.config.save() self.config.parser.load() expected = UnspacedList(_redirect_block_for_domain("www.nomatch.com"))[0] generated_conf = self.config.parser.parsed[default_conf] self.assertTrue(util.contains_at_depth(generated_conf, expected, 2)) @mock.patch('certbot.reverter.logger') @mock.patch('certbot_nginx.parser.NginxParser.load') def test_parser_reload_after_config_changes(self, mock_parser_load, unused_mock_logger): self.config.recovery_routine() self.config.revert_challenge_config() self.config.rollback_checkpoints() self.assertTrue(mock_parser_load.call_count == 3) def test_choose_vhosts_wildcard(self): # pylint: disable=protected-access mock_path = "certbot_nginx.display_ops.select_vhost_multiple" with mock.patch(mock_path) as mock_select_vhs: vhost = [x for x in self.config.parser.get_vhosts() if 'summer.com' in x.names][0] mock_select_vhs.return_value = [vhost] vhs = self.config._choose_vhosts_wildcard("*.com", prefer_ssl=True) # Check that the dialog was called with migration.com self.assertTrue(vhost in mock_select_vhs.call_args[0][0]) # And the actual returned values self.assertEquals(len(vhs), 1) self.assertEqual(vhs[0], vhost) def test_choose_vhosts_wildcard_redirect(self): # pylint: disable=protected-access mock_path = "certbot_nginx.display_ops.select_vhost_multiple" with mock.patch(mock_path) as mock_select_vhs: vhost = [x for x in self.config.parser.get_vhosts() if 'summer.com' in x.names][0] mock_select_vhs.return_value = [vhost] vhs = self.config._choose_vhosts_wildcard("*.com", prefer_ssl=False) # Check that the dialog was called with migration.com self.assertTrue(vhost in mock_select_vhs.call_args[0][0]) # And the actual returned values self.assertEquals(len(vhs), 1) self.assertEqual(vhs[0], vhost) def test_deploy_cert_wildcard(self): # pylint: disable=protected-access mock_choose_vhosts = mock.MagicMock() vhost = [x for x in self.config.parser.get_vhosts() if 'geese.com' in x.names][0] mock_choose_vhosts.return_value = [vhost] self.config._choose_vhosts_wildcard = mock_choose_vhosts mock_d = "certbot_nginx.configurator.NginxConfigurator._deploy_cert" with mock.patch(mock_d) as mock_dep: self.config.deploy_cert("*.com", "/tmp/path", "/tmp/path", "/tmp/path", "/tmp/path") self.assertTrue(mock_dep.called) self.assertEquals(len(mock_dep.call_args_list), 1) self.assertEqual(vhost, mock_dep.call_args_list[0][0][0]) @mock.patch("certbot_nginx.display_ops.select_vhost_multiple") def test_deploy_cert_wildcard_no_vhosts(self, mock_dialog): # pylint: disable=protected-access mock_dialog.return_value = [] self.assertRaises(errors.PluginError, self.config.deploy_cert, "*.wild.cat", "/tmp/path", "/tmp/path", "/tmp/path", "/tmp/path") @mock.patch("certbot_nginx.display_ops.select_vhost_multiple") def test_enhance_wildcard_ocsp_after_install(self, mock_dialog): # pylint: disable=protected-access vhost = [x for x in self.config.parser.get_vhosts() if 'geese.com' in x.names][0] self.config._wildcard_vhosts["*.com"] = [vhost] self.config.enhance("*.com", "staple-ocsp", "example/chain.pem") self.assertFalse(mock_dialog.called) @mock.patch("certbot_nginx.display_ops.select_vhost_multiple") def test_enhance_wildcard_redirect_or_ocsp_no_install(self, mock_dialog): vhost = [x for x in self.config.parser.get_vhosts() if 'summer.com' in x.names][0] mock_dialog.return_value = [vhost] self.config.enhance("*.com", "staple-ocsp", "example/chain.pem") self.assertTrue(mock_dialog.called) @mock.patch("certbot_nginx.display_ops.select_vhost_multiple") def test_enhance_wildcard_double_redirect(self, mock_dialog): # pylint: disable=protected-access vhost = [x for x in self.config.parser.get_vhosts() if 'summer.com' in x.names][0] self.config._wildcard_redirect_vhosts["*.com"] = [vhost] self.config.enhance("*.com", "redirect") self.assertFalse(mock_dialog.called) def test_choose_vhosts_wildcard_no_ssl_filter_port(self): # pylint: disable=protected-access mock_path = "certbot_nginx.display_ops.select_vhost_multiple" with mock.patch(mock_path) as mock_select_vhs: mock_select_vhs.return_value = [] self.config._choose_vhosts_wildcard("*.com", prefer_ssl=False, no_ssl_filter_port='80') # Check that the dialog was called with only port 80 vhosts self.assertEqual(len(mock_select_vhs.call_args[0][0]), 4) class InstallSslOptionsConfTest(util.NginxTest): """Test that the options-ssl-nginx.conf file is installed and updated properly.""" def setUp(self): super(InstallSslOptionsConfTest, self).setUp() self.config = util.get_nginx_configurator( self.config_path, self.config_dir, self.work_dir, self.logs_dir) def _call(self): from certbot_nginx.configurator import install_ssl_options_conf install_ssl_options_conf(self.config.mod_ssl_conf, self.config.updated_mod_ssl_conf_digest) def _current_ssl_options_hash(self): from certbot_nginx.constants import MOD_SSL_CONF_SRC return crypto_util.sha256sum(MOD_SSL_CONF_SRC) def _assert_current_file(self): self.assertTrue(os.path.isfile(self.config.mod_ssl_conf)) self.assertEqual(crypto_util.sha256sum(self.config.mod_ssl_conf), self._current_ssl_options_hash()) def test_no_file(self): # prepare should have placed a file there self._assert_current_file() os.remove(self.config.mod_ssl_conf) self.assertFalse(os.path.isfile(self.config.mod_ssl_conf)) self._call() self._assert_current_file() def test_current_file(self): self._assert_current_file() self._call() self._assert_current_file() def test_prev_file_updates_to_current(self): from certbot_nginx.constants import ALL_SSL_OPTIONS_HASHES with mock.patch('certbot.crypto_util.sha256sum') as mock_sha256: mock_sha256.return_value = ALL_SSL_OPTIONS_HASHES[0] self._call() self._assert_current_file() def test_manually_modified_current_file_does_not_update(self): with open(self.config.mod_ssl_conf, "a") as mod_ssl_conf: mod_ssl_conf.write("a new line for the wrong hash\n") with mock.patch("certbot.plugins.common.logger") as mock_logger: self._call() self.assertFalse(mock_logger.warning.called) self.assertTrue(os.path.isfile(self.config.mod_ssl_conf)) self.assertEqual(crypto_util.sha256sum(constants.MOD_SSL_CONF_SRC), self._current_ssl_options_hash()) self.assertNotEqual(crypto_util.sha256sum(self.config.mod_ssl_conf), self._current_ssl_options_hash()) def test_manually_modified_past_file_warns(self): with open(self.config.mod_ssl_conf, "a") as mod_ssl_conf: mod_ssl_conf.write("a new line for the wrong hash\n") with open(self.config.updated_mod_ssl_conf_digest, "w") as f: f.write("hashofanoldversion") with mock.patch("certbot.plugins.common.logger") as mock_logger: self._call() self.assertEqual(mock_logger.warning.call_args[0][0], "%s has been manually modified; updated file " "saved to %s. We recommend updating %s for security purposes.") self.assertEqual(crypto_util.sha256sum(constants.MOD_SSL_CONF_SRC), self._current_ssl_options_hash()) # only print warning once with mock.patch("certbot.plugins.common.logger") as mock_logger: self._call() self.assertFalse(mock_logger.warning.called) def test_current_file_hash_in_all_hashes(self): from certbot_nginx.constants import ALL_SSL_OPTIONS_HASHES self.assertTrue(self._current_ssl_options_hash() in ALL_SSL_OPTIONS_HASHES, "Constants.ALL_SSL_OPTIONS_HASHES must be appended" " with the sha256 hash of self.config.mod_ssl_conf when it is updated.") if __name__ == "__main__": unittest.main() # pragma: no cover certbot-nginx-0.23.0/certbot_nginx/tests/util.py0000644000076600000240000000747113261244762021646 0ustar bmwstaff00000000000000"""Common utilities for certbot_nginx.""" import copy import os import pkg_resources import tempfile import unittest import josepy as jose import mock import zope.component from certbot import configuration from certbot.tests import util as test_util from certbot.plugins import common from certbot_nginx import configurator from certbot_nginx import nginxparser class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods def setUp(self): super(NginxTest, self).setUp() self.temp_dir, self.config_dir, self.work_dir = common.dir_setup( "etc_nginx", "certbot_nginx.tests") self.logs_dir = tempfile.mkdtemp('logs') self.config_path = os.path.join(self.temp_dir, "etc_nginx") self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector( "rsa512_key.pem")) def get_data_filename(filename): """Gets the filename of a test data file.""" return pkg_resources.resource_filename( "certbot_nginx.tests", os.path.join( "testdata", "etc_nginx", filename)) def get_nginx_configurator( config_path, config_dir, work_dir, logs_dir, version=(1, 6, 2)): """Create an Nginx Configurator with the specified options.""" backups = os.path.join(work_dir, "backups") with mock.patch("certbot_nginx.configurator.NginxConfigurator." "config_test"): with mock.patch("certbot_nginx.configurator.util." "exe_exists") as mock_exe_exists: mock_exe_exists.return_value = True config = configurator.NginxConfigurator( config=mock.MagicMock( nginx_server_root=config_path, le_vhost_ext="-le-ssl.conf", config_dir=config_dir, work_dir=work_dir, logs_dir=logs_dir, backup_dir=backups, temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"), in_progress_dir=os.path.join(backups, "IN_PROGRESS"), server="https://acme-server.org:443/new", tls_sni_01_port=5001, http01_port=80 ), name="nginx", version=version) config.prepare() # Provide general config utility. nsconfig = configuration.NamespaceConfig(config.config) zope.component.provideUtility(nsconfig) return config def filter_comments(tree): """Filter comment nodes from parsed configurations.""" def traverse(tree): """Generator dropping comment nodes""" for entry in tree: # key, values = entry spaceless = [e for e in entry if not nginxparser.spacey(e)] if spaceless: key = spaceless[0] values = spaceless[1] if len(spaceless) > 1 else None else: key = values = "" if isinstance(key, list): new = copy.deepcopy(entry) new[1] = filter_comments(values) yield new else: if key != '#' and spaceless: yield spaceless return list(traverse(tree)) def contains_at_depth(haystack, needle, n): """Is the needle in haystack at depth n? Return true if the needle is present in one of the sub-iterables in haystack at depth n. Haystack must be an iterable. """ # Specifically use hasattr rather than isinstance(..., collections.Iterable) # because we want to include lists but reject strings. if not hasattr(haystack, '__iter__') or hasattr(haystack, 'strip'): return False if n == 0: return needle in haystack else: for item in haystack: if contains_at_depth(item, needle, n - 1): return True return False certbot-nginx-0.23.0/certbot_nginx/tests/__init__.py0000644000076600000240000000003213261244762022412 0ustar bmwstaff00000000000000"""Certbot Nginx Tests""" certbot-nginx-0.23.0/certbot_nginx/tests/tls_sni_01_test.py0000644000076600000240000001414313261244762023675 0ustar bmwstaff00000000000000"""Tests for certbot_nginx.tls_sni_01""" import unittest import shutil import mock import six from acme import challenges from certbot import achallenges from certbot import errors from certbot.plugins import common_test from certbot.tests import acme_util from certbot_nginx import obj from certbot_nginx.tests import util class TlsSniPerformTest(util.NginxTest): """Test the NginxTlsSni01 challenge.""" account_key = common_test.AUTH_KEY achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token=b"kNdwjwOeX0I_A8DXt9Msmg"), "pending"), domain="www.example.com", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01( token=b"\xba\xa9\xda?= 1 and x[0] == 2, lambda x, y, pts=paths: pts.append(y)) self.assertEqual(paths, result) def test_get_vhosts_global_ssl(self): nparser = parser.NginxParser(self.config_path) vhosts = nparser.get_vhosts() vhost = obj.VirtualHost(nparser.abs_path('sites-enabled/globalssl.com'), [obj.Addr('4.8.2.6', '57', True, False, False, False)], True, True, set(['globalssl.com']), [], [0]) globalssl_com = [x for x in vhosts if 'globalssl.com' in x.filep][0] self.assertEqual(vhost, globalssl_com) def test_get_vhosts(self): nparser = parser.NginxParser(self.config_path) vhosts = nparser.get_vhosts() vhost1 = obj.VirtualHost(nparser.abs_path('nginx.conf'), [obj.Addr('', '8080', False, False, False, False)], False, True, set(['localhost', r'~^(www\.)?(example|bar)\.']), [], [10, 1, 9]) vhost2 = obj.VirtualHost(nparser.abs_path('nginx.conf'), [obj.Addr('somename', '8080', False, False, False, False), obj.Addr('', '8000', False, False, False, False)], False, True, set(['somename', 'another.alias', 'alias']), [], [10, 1, 12]) vhost3 = obj.VirtualHost(nparser.abs_path('sites-enabled/example.com'), [obj.Addr('69.50.225.155', '9000', False, False, False, False), obj.Addr('127.0.0.1', '', False, False, False, False)], False, True, set(['.example.com', 'example.*']), [], [0]) vhost4 = obj.VirtualHost(nparser.abs_path('sites-enabled/default'), [obj.Addr('myhost', '', False, True, False, False), obj.Addr('otherhost', '', False, True, False, False)], False, True, set(['www.example.org']), [], [0]) vhost5 = obj.VirtualHost(nparser.abs_path('foo.conf'), [obj.Addr('*', '80', True, True, False, False)], True, True, set(['*.www.foo.com', '*.www.example.com']), [], [2, 1, 0]) self.assertEqual(12, len(vhosts)) example_com = [x for x in vhosts if 'example.com' in x.filep][0] self.assertEqual(vhost3, example_com) default = [x for x in vhosts if 'default' in x.filep][0] self.assertEqual(vhost4, default) fooconf = [x for x in vhosts if 'foo.conf' in x.filep][0] self.assertEqual(vhost5, fooconf) localhost = [x for x in vhosts if 'localhost' in x.names][0] self.assertEqual(vhost1, localhost) somename = [x for x in vhosts if 'somename' in x.names][0] self.assertEqual(vhost2, somename) def test_has_ssl_on_directive(self): nparser = parser.NginxParser(self.config_path) mock_vhost = obj.VirtualHost(None, None, None, None, None, [['listen', 'myhost default_server'], ['server_name', 'www.example.org'], [['location', '/'], [['root', 'html'], ['index', 'index.html index.htm']]] ], None) self.assertFalse(nparser.has_ssl_on_directive(mock_vhost)) mock_vhost.raw = [['listen', '*:80', 'default_server', 'ssl'], ['server_name', '*.www.foo.com', '*.www.example.com'], ['root', '/home/ubuntu/sites/foo/']] self.assertFalse(nparser.has_ssl_on_directive(mock_vhost)) mock_vhost.raw = [['listen', '80 ssl'], ['server_name', '*.www.foo.com', '*.www.example.com']] self.assertFalse(nparser.has_ssl_on_directive(mock_vhost)) mock_vhost.raw = [['listen', '80'], ['ssl', 'on'], ['server_name', '*.www.foo.com', '*.www.example.com']] self.assertTrue(nparser.has_ssl_on_directive(mock_vhost)) def test_remove_server_directives(self): nparser = parser.NginxParser(self.config_path) mock_vhost = obj.VirtualHost(nparser.abs_path('nginx.conf'), None, None, None, set(['localhost', r'~^(www\.)?(example|bar)\.']), None, [10, 1, 9]) example_com = nparser.abs_path('sites-enabled/example.com') names = set(['.example.com', 'example.*']) mock_vhost.filep = example_com mock_vhost.names = names mock_vhost.path = [0] nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['ssl_certificate', '/etc/ssl/cert2.pem']]) nparser.remove_server_directives(mock_vhost, 'foo') nparser.remove_server_directives(mock_vhost, 'ssl_certificate') self.assertEqual(nparser.parsed[example_com], [[['server'], [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], []]]]) def test_add_server_directives(self): nparser = parser.NginxParser(self.config_path) mock_vhost = obj.VirtualHost(nparser.abs_path('nginx.conf'), None, None, None, set(['localhost', r'~^(www\.)?(example|bar)\.']), None, [10, 1, 9]) nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['\n ', 'ssl_certificate', ' ', '/etc/ssl/cert.pem']]) ssl_re = re.compile(r'\n\s+ssl_certificate /etc/ssl/cert.pem') dump = nginxparser.dumps(nparser.parsed[nparser.abs_path('nginx.conf')]) self.assertEqual(1, len(re.findall(ssl_re, dump))) example_com = nparser.abs_path('sites-enabled/example.com') names = set(['.example.com', 'example.*']) mock_vhost.filep = example_com mock_vhost.names = names mock_vhost.path = [0] nparser.add_server_directives(mock_vhost, [['foo', 'bar'], ['ssl_certificate', '/etc/ssl/cert2.pem']]) nparser.add_server_directives(mock_vhost, [['foo', 'bar']]) from certbot_nginx.parser import COMMENT self.assertEqual(nparser.parsed[example_com], [[['server'], [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], ['foo', 'bar'], ['#', COMMENT], ['ssl_certificate', '/etc/ssl/cert2.pem'], ['#', COMMENT], [], [] ]]]) server_conf = nparser.abs_path('server.conf') names = set(['alias', 'another.alias', 'somename']) mock_vhost.filep = server_conf mock_vhost.names = names mock_vhost.path = [] self.assertRaises(errors.MisconfigurationError, nparser.add_server_directives, mock_vhost, [['foo', 'bar'], ['ssl_certificate', '/etc/ssl/cert2.pem']]) def test_comment_is_repeatable(self): nparser = parser.NginxParser(self.config_path) example_com = nparser.abs_path('sites-enabled/example.com') mock_vhost = obj.VirtualHost(example_com, None, None, None, set(['.example.com', 'example.*']), None, [0]) nparser.add_server_directives(mock_vhost, [['\n ', '#', ' ', 'what a nice comment']]) nparser.add_server_directives(mock_vhost, [['\n ', 'include', ' ', nparser.abs_path('comment_in_file.conf')]]) from certbot_nginx.parser import COMMENT self.assertEqual(nparser.parsed[example_com], [[['server'], [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], ['#', ' ', 'what a nice comment'], [], ['include', nparser.abs_path('comment_in_file.conf')], ['#', COMMENT], []]]] ) def test_replace_server_directives(self): nparser = parser.NginxParser(self.config_path) target = set(['.example.com', 'example.*']) filep = nparser.abs_path('sites-enabled/example.com') mock_vhost = obj.VirtualHost(filep, None, None, None, target, None, [0]) nparser.update_or_add_server_directives( mock_vhost, [['server_name', 'foobar.com']]) from certbot_nginx.parser import COMMENT self.assertEqual( nparser.parsed[filep], [[['server'], [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', 'foobar.com'], ['#', COMMENT], ['server_name', 'example.*'], [] ]]]) mock_vhost.names = set(['foobar.com', 'example.*']) nparser.update_or_add_server_directives( mock_vhost, [['ssl_certificate', 'cert.pem']]) self.assertEqual( nparser.parsed[filep], [[['server'], [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', 'foobar.com'], ['#', COMMENT], ['server_name', 'example.*'], [], ['ssl_certificate', 'cert.pem'], ['#', COMMENT], [], ]]]) def test_get_best_match(self): target_name = 'www.eff.org' names = [set(['www.eff.org', 'irrelevant.long.name.eff.org', '*.org']), set(['eff.org', 'ww2.eff.org', 'test.www.eff.org']), set(['*.eff.org', '.www.eff.org']), set(['.eff.org', '*.org']), set(['www.eff.', 'www.eff.*', '*.www.eff.org']), set(['example.com', r'~^(www\.)?(eff.+)', '*.eff.*']), set(['*', r'~^(www\.)?(eff.+)']), set(['www.*', r'~^(www\.)?(eff.+)', '.test.eff.org']), set(['*.org', r'*.eff.org', 'www.eff.*']), set(['*.www.eff.org', 'www.*']), set(['*.org']), set([]), set(['example.com'])] winners = [('exact', 'www.eff.org'), (None, None), ('exact', '.www.eff.org'), ('wildcard_start', '.eff.org'), ('wildcard_end', 'www.eff.*'), ('regex', r'~^(www\.)?(eff.+)'), ('wildcard_start', '*'), ('wildcard_end', 'www.*'), ('wildcard_start', '*.eff.org'), ('wildcard_end', 'www.*'), ('wildcard_start', '*.org'), (None, None), (None, None)] for i, winner in enumerate(winners): self.assertEqual(winner, parser.get_best_match(target_name, names[i])) def test_comment_directive(self): # pylint: disable=protected-access block = nginxparser.UnspacedList([ ["\n", "a", " ", "b", "\n"], ["c", " ", "d"], ["\n", "e", " ", "f"]]) from certbot_nginx.parser import comment_directive, COMMENT_BLOCK comment_directive(block, 1) comment_directive(block, 0) self.assertEqual(block.spaced, [ ["\n", "a", " ", "b", "\n"], COMMENT_BLOCK, "\n", ["c", " ", "d"], COMMENT_BLOCK, ["\n", "e", " ", "f"]]) def test_comment_out_directive(self): server_block = nginxparser.loads(""" server { listen 80; root /var/www/html; index star.html; server_name *.functorkitten.xyz; ssl_session_timeout 1440m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; }""") block = server_block[0][1] from certbot_nginx.parser import _comment_out_directive _comment_out_directive(block, 4, "blah1") _comment_out_directive(block, 5, "blah2") _comment_out_directive(block, 6, "blah3") self.assertEqual(block.spaced, [ ['\n ', 'listen', ' ', '80'], ['\n ', 'root', ' ', '/var/www/html'], ['\n ', 'index', ' ', 'star.html'], ['\n\n ', 'server_name', ' ', '*.functorkitten.xyz'], ['\n ', '#', ' ssl_session_timeout 1440m; # duplicated in blah1'], [' ', '#', ' ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # duplicated in blah2'], ['\n\n ', '#', ' ssl_prefer_server_ciphers on; # duplicated in blah3'], '\n ']) def test_parse_server_raw_ssl(self): server = parser._parse_server_raw([ #pylint: disable=protected-access ['listen', '443'] ]) self.assertFalse(server['ssl']) server = parser._parse_server_raw([ #pylint: disable=protected-access ['listen', '443', 'ssl'] ]) self.assertTrue(server['ssl']) server = parser._parse_server_raw([ #pylint: disable=protected-access ['listen', '443'], ['ssl', 'off'] ]) self.assertFalse(server['ssl']) server = parser._parse_server_raw([ #pylint: disable=protected-access ['listen', '443'], ['ssl', 'on'] ]) self.assertTrue(server['ssl']) def test_parse_server_raw_unix(self): server = parser._parse_server_raw([ #pylint: disable=protected-access ['listen', 'unix:/var/run/nginx.sock'] ]) self.assertEqual(len(server['addrs']), 0) def test_parse_server_global_ssl_applied(self): nparser = parser.NginxParser(self.config_path) server = nparser.parse_server([ ['listen', '443'] ]) self.assertTrue(server['ssl']) def test_duplicate_vhost(self): nparser = parser.NginxParser(self.config_path) vhosts = nparser.get_vhosts() default = [x for x in vhosts if 'default' in x.filep][0] new_vhost = nparser.duplicate_vhost(default, remove_singleton_listen_params=True) nparser.filedump(ext='') # check properties of new vhost self.assertFalse(next(iter(new_vhost.addrs)).default) self.assertNotEqual(new_vhost.path, default.path) # check that things are written to file correctly new_nparser = parser.NginxParser(self.config_path) new_vhosts = new_nparser.get_vhosts() new_defaults = [x for x in new_vhosts if 'default' in x.filep] self.assertEqual(len(new_defaults), 2) new_vhost_parsed = new_defaults[1] self.assertFalse(next(iter(new_vhost_parsed.addrs)).default) self.assertEqual(next(iter(default.names)), next(iter(new_vhost_parsed.names))) self.assertEqual(len(default.raw), len(new_vhost_parsed.raw)) self.assertTrue(next(iter(default.addrs)).super_eq(next(iter(new_vhost_parsed.addrs)))) def test_duplicate_vhost_remove_ipv6only(self): nparser = parser.NginxParser(self.config_path) vhosts = nparser.get_vhosts() ipv6ssl = [x for x in vhosts if 'ipv6ssl' in x.filep][0] new_vhost = nparser.duplicate_vhost(ipv6ssl, remove_singleton_listen_params=True) nparser.filedump(ext='') for addr in new_vhost.addrs: self.assertFalse(addr.ipv6only) identical_vhost = nparser.duplicate_vhost(ipv6ssl, remove_singleton_listen_params=False) nparser.filedump(ext='') called = False for addr in identical_vhost.addrs: if addr.ipv6: self.assertTrue(addr.ipv6only) called = True self.assertTrue(called) if __name__ == "__main__": unittest.main() # pragma: no cover certbot-nginx-0.23.0/certbot_nginx/tests/nginxparser_test.py0000644000076600000240000004044413261244762024265 0ustar bmwstaff00000000000000"""Test for certbot_nginx.nginxparser.""" import copy import operator import tempfile import unittest from pyparsing import ParseException from certbot_nginx.nginxparser import ( RawNginxParser, loads, load, dumps, dump, UnspacedList) from certbot_nginx.tests import util FIRST = operator.itemgetter(0) class TestRawNginxParser(unittest.TestCase): """Test the raw low-level Nginx config parser.""" def test_assignments(self): parsed = RawNginxParser.assignment.parseString('root /test;').asList() self.assertEqual(parsed, ['root', ' ', '/test']) parsed = RawNginxParser.assignment.parseString('root /test;foo bar;').asList() self.assertEqual(parsed, ['root', ' ', '/test'], ['foo', ' ', 'bar']) def test_blocks(self): parsed = RawNginxParser.block.parseString('foo {}').asList() self.assertEqual(parsed, [['foo', ' '], []]) parsed = RawNginxParser.block.parseString('location /foo{}').asList() self.assertEqual(parsed, [['location', ' ', '/foo'], []]) parsed = RawNginxParser.block.parseString('foo { bar foo ; }').asList() self.assertEqual(parsed, [['foo', ' '], [[' ', 'bar', ' ', 'foo', ' '], ' ']]) def test_nested_blocks(self): parsed = RawNginxParser.block.parseString('foo { bar {} }').asList() block, content = parsed self.assertEqual(FIRST(content), [[' ', 'bar', ' '], []]) self.assertEqual(FIRST(block), 'foo') def test_dump_as_string(self): dumped = dumps(UnspacedList([ ['user', ' ', 'www-data'], [['\n', 'server', ' '], [ ['\n ', 'listen', ' ', '80'], ['\n ', 'server_name', ' ', 'foo.com'], ['\n ', 'root', ' ', '/home/ubuntu/sites/foo/'], [['\n\n ', 'location', ' ', '/status', ' '], [ ['\n ', 'check_status', ''], [['\n\n ', 'types', ' '], [['\n ', 'image/jpeg', ' ', 'jpg']]], ]] ]]])) self.assertEqual(dumped.split('\n'), 'user www-data;\n' 'server {\n' ' listen 80;\n' ' server_name foo.com;\n' ' root /home/ubuntu/sites/foo/;\n' '\n' ' location /status {\n' ' check_status;\n' '\n' ' types {\n' ' image/jpeg jpg;}}}'.split('\n')) def test_parse_from_file(self): with open(util.get_data_filename('foo.conf')) as handle: parsed = util.filter_comments(load(handle)) self.assertEqual( parsed, [['user', 'www-data'], [['http'], [[['server'], [ ['listen', '*:80', 'default_server', 'ssl'], ['server_name', '*.www.foo.com', '*.www.example.com'], ['root', '/home/ubuntu/sites/foo/'], [['location', '/status'], [ [['types'], [['image/jpeg', 'jpg']]], ]], [['location', '~', r'case_sensitive\.php$'], [ ['index', 'index.php'], ['root', '/var/root'], ]], [['location', '~*', r'case_insensitive\.php$'], []], [['location', '=', r'exact_match\.php$'], []], [['location', '^~', r'ignore_regex\.php$'], []] ]]]]] ) def test_parse_from_file2(self): with open(util.get_data_filename('edge_cases.conf')) as handle: parsed = util.filter_comments(load(handle)) self.assertEqual( parsed, [[['server'], [['server_name', 'simple']]], [['server'], [['server_name', 'with.if'], [['location', '~', '^/services/.+$'], [[['if', '($request_filename', '~*', '\\.(ttf|woff)$)'], [['add_header', 'Access-Control-Allow-Origin', '"*"']]]]]]], [['server'], [['server_name', 'with.complicated.headers'], [['location', '~*', '\\.(?:gif|jpe?g|png)$'], [['add_header', 'Pragma', 'public'], ['add_header', 'Cache-Control', '\'public, must-revalidate, proxy-revalidate\'', '"test,;{}"', 'foo'], ['blah', '"hello;world"'], ['try_files', '$uri', '@rewrites']]]]]]) def test_parse_from_file3(self): with open(util.get_data_filename('multiline_quotes.conf')) as handle: parsed = util.filter_comments(load(handle)) self.assertEqual( parsed, [[['http'], [[['server'], [['listen', '*:443'], [['location', '/'], [['body_filter_by_lua', '\'ngx.ctx.buffered = (ngx.ctx.buffered or "")' ' .. string.sub(ngx.arg[1], 1, 1000)\n' ' ' 'if ngx.arg[2] then\n' ' ' 'ngx.var.resp_body = ngx.ctx.buffered\n' ' end\'']]]]]]]]) def test_abort_on_parse_failure(self): with open(util.get_data_filename('broken.conf')) as handle: self.assertRaises(ParseException, load, handle) def test_dump_as_file(self): with open(util.get_data_filename('nginx.conf')) as handle: parsed = load(handle) parsed[-1][-1].append(UnspacedList([['server'], [['listen', ' ', '443', ' ', 'ssl'], ['server_name', ' ', 'localhost'], ['ssl_certificate', ' ', 'cert.pem'], ['ssl_certificate_key', ' ', 'cert.key'], ['ssl_session_cache', ' ', 'shared:SSL:1m'], ['ssl_session_timeout', ' ', '5m'], ['ssl_ciphers', ' ', 'HIGH:!aNULL:!MD5'], [['location', ' ', '/'], [['root', ' ', 'html'], ['index', ' ', 'index.html', ' ', 'index.htm']]]]])) with tempfile.TemporaryFile(mode='w+t') as f: dump(parsed, f) f.seek(0) parsed_new = load(f) self.assertEqual(parsed, parsed_new) def test_comments(self): with open(util.get_data_filename('minimalistic_comments.conf')) as handle: parsed = load(handle) with tempfile.TemporaryFile(mode='w+t') as f: dump(parsed, f) f.seek(0) parsed_new = load(f) self.assertEqual(parsed, parsed_new) self.assertEqual(parsed_new, [ ['#', " Use bar.conf when it's a full moon!"], ['include', 'foo.conf'], ['#', ' Kilroy was here'], ['check_status'], [['server'], [['#', ''], ['#', " Don't forget to open up your firewall!"], ['#', ''], ['listen', '1234'], ['#', ' listen 80;']]], ]) def test_issue_518(self): parsed = loads('if ($http_accept ~* "webp") { set $webp "true"; }') self.assertEqual(parsed, [ [['if', '($http_accept', '~*', '"webp")'], [['set', '$webp', '"true"']]] ]) def test_comment_in_block(self): parsed = loads("""http { # server{ }""") self.assertEqual(parsed, [ [['http'], [['#', ' server{']]] ]) def test_access_log(self): # see issue #3798 parsed = loads('access_log syslog:server=unix:/dev/log,facility=auth,' 'tag=nginx_post,severity=info custom;') self.assertEqual(parsed, [ ['access_log', 'syslog:server=unix:/dev/log,facility=auth,tag=nginx_post,severity=info', 'custom'] ]) def test_add_header(self): # see issue #3798 parsed = loads('add_header Cache-Control no-cache,no-store,must-revalidate,max-age=0;') self.assertEqual(parsed, [ ['add_header', 'Cache-Control', 'no-cache,no-store,must-revalidate,max-age=0'] ]) def test_map_then_assignment_in_block(self): # see issue #3798 test_str = """http { map $http_upgrade $connection_upgrade { default upgrade; '' close; "~Opera Mini" 1; *.example.com 1; } one; }""" parsed = loads(test_str) self.assertEqual(parsed, [ [['http'], [ [['map', '$http_upgrade', '$connection_upgrade'], [ ['default', 'upgrade'], ["''", 'close'], ['"~Opera Mini"', '1'], ['*.example.com', '1'] ]], ['one'] ]] ]) def test_variable_name(self): parsed = loads('try_files /typo3temp/tx_ncstaticfilecache/' '$host${request_uri}index.html @nocache;') self.assertEqual(parsed, [ ['try_files', '/typo3temp/tx_ncstaticfilecache/$host${request_uri}index.html', '@nocache'] ]) def test_weird_blocks(self): test = r""" if ($http_user_agent ~ MSIE) { rewrite ^(.*)$ /msie/$1 break; } if ($http_cookie ~* "id=([^;]+)(?:;|$)") { set $id $1; } if ($request_method = POST) { return 405; } if ($request_method) { return 403; } if ($args ~ post=140){ rewrite ^ http://example.com/; } location ~ ^/users/(.+\.(?:gif|jpe?g|png))$ { alias /data/w3/images/$1; } """ parsed = loads(test) self.assertEqual(parsed, [[['if', '($http_user_agent', '~', 'MSIE)'], [['rewrite', '^(.*)$', '/msie/$1', 'break']]], [['if', '($http_cookie', '~*', '"id=([^;]+)(?:;|$)")'], [['set', '$id', '$1']]], [['if', '($request_method', '=', 'POST)'], [['return', '405']]], [['if', '($request_method)'], [['return', '403']]], [['if', '($args', '~', 'post=140)'], [['rewrite', '^', 'http://example.com/']]], [['location', '~', '^/users/(.+\\.(?:gif|jpe?g|png))$'], [['alias', '/data/w3/images/$1']]]] ) def test_edge_cases(self): # quotes parsed = loads(r'"hello\""; # blah "heh heh"') self.assertEqual(parsed, [['"hello\\""'], ['#', ' blah "heh heh"']]) # empty var as block parsed = loads(r"${}") self.assertEqual(parsed, [[['$'], []]]) # if with comment parsed = loads("""if ($http_cookie ~* "id=([^;]+)(?:;|$)") { # blah ) }""") self.assertEqual(parsed, [[['if', '($http_cookie', '~*', '"id=([^;]+)(?:;|$)")'], [['#', ' blah )']]]]) # end paren test = """ one"test"; ("two"); "test")red; "test")"blue"; "test")"three; (one"test")one; one"; one"test; one"test"one; """ parsed = loads(test) self.assertEqual(parsed, [ ['one"test"'], ['("two")'], ['"test")red'], ['"test")"blue"'], ['"test")"three'], ['(one"test")one'], ['one"'], ['one"test'], ['one"test"one'] ]) self.assertRaises(ParseException, loads, r'"test"one;') # fails self.assertRaises(ParseException, loads, r'"test;') # fails # newlines test = """ server_name foo.example.com bar.example.com \ baz.example.com qux.example.com; server_name foo.example.com bar.example.com baz.example.com qux.example.com; """ parsed = loads(test) self.assertEqual(parsed, [ ['server_name', 'foo.example.com', 'bar.example.com', 'baz.example.com', 'qux.example.com'], ['server_name', 'foo.example.com', 'bar.example.com', 'baz.example.com', 'qux.example.com'] ]) # variable weirdness parsed = loads("directive $var;") self.assertEqual(parsed, [['directive', '$var']]) self.assertRaises(ParseException, loads, "server {server_name test.com};") self.assertRaises(ParseException, loads, "directive ${var};") self.assertEqual(loads("blag${dfgdfg};"), [['blag${dfgdfg}']]) self.assertRaises(ParseException, loads, "blag${dfgdf{g};") class TestUnspacedList(unittest.TestCase): """Test the UnspacedList data structure""" def setUp(self): self.a = ["\n ", "things", " ", "quirk"] self.b = ["y", " "] self.l = self.a[:] self.l2 = self.b[:] self.ul = UnspacedList(self.l) self.ul2 = UnspacedList(self.l2) def test_construction(self): self.assertEqual(self.ul, ["things", "quirk"]) self.assertEqual(self.ul2, ["y"]) def test_append(self): ul3 = copy.deepcopy(self.ul) ul3.append("wise") self.assertEqual(ul3, ["things", "quirk", "wise"]) self.assertEqual(ul3.spaced, self.a + ["wise"]) def test_add(self): ul3 = self.ul + self.ul2 self.assertEqual(ul3, ["things", "quirk", "y"]) self.assertEqual(ul3.spaced, self.a + self.b) self.assertEqual(self.ul.spaced, self.a) ul3 = self.ul + self.l2 self.assertEqual(ul3, ["things", "quirk", "y"]) self.assertEqual(ul3.spaced, self.a + self.b) def test_extend(self): ul3 = copy.deepcopy(self.ul) ul3.extend(self.ul2) self.assertEqual(ul3, ["things", "quirk", "y"]) self.assertEqual(ul3.spaced, self.a + self.b) self.assertEqual(self.ul.spaced, self.a) def test_set(self): ul3 = copy.deepcopy(self.ul) ul3[0] = "zither" l = ["\n ", "zather", "zest"] ul3[1] = UnspacedList(l) self.assertEqual(ul3, ["zither", ["zather", "zest"]]) self.assertEqual(ul3.spaced, [self.a[0], "zither", " ", l]) def test_get(self): self.assertRaises(IndexError, self.ul2.__getitem__, 2) self.assertRaises(IndexError, self.ul2.__getitem__, -3) def test_insert(self): x = UnspacedList( [['\n ', 'listen', ' ', '69.50.225.155:9000'], ['\n ', 'listen', ' ', '127.0.0.1'], ['\n ', 'server_name', ' ', '.example.com'], ['\n ', 'server_name', ' ', 'example.*'], '\n', ['listen', ' ', '5001', ' ', 'ssl']]) x.insert(5, "FROGZ") self.assertEqual(x, [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'], ['server_name', '.example.com'], ['server_name', 'example.*'], ['listen', '5001', 'ssl'], 'FROGZ']) self.assertEqual(x.spaced, [['\n ', 'listen', ' ', '69.50.225.155:9000'], ['\n ', 'listen', ' ', '127.0.0.1'], ['\n ', 'server_name', ' ', '.example.com'], ['\n ', 'server_name', ' ', 'example.*'], '\n', ['listen', ' ', '5001', ' ', 'ssl'], 'FROGZ']) def test_rawlists(self): ul3 = copy.deepcopy(self.ul) ul3.insert(0, "some") ul3.append("why") ul3.extend(["did", "whether"]) del ul3[2] self.assertEqual(ul3, ["some", "things", "why", "did", "whether"]) def test_is_dirty(self): self.assertEqual(False, self.ul2.is_dirty()) ul3 = UnspacedList([]) ul3.append(self.ul) self.assertEqual(False, self.ul.is_dirty()) self.assertEqual(True, ul3.is_dirty()) ul4 = UnspacedList([[1], [2, 3, 4]]) self.assertEqual(False, ul4.is_dirty()) ul4[1][2] = 5 self.assertEqual(True, ul4.is_dirty()) if __name__ == '__main__': unittest.main() # pragma: no cover certbot-nginx-0.23.0/certbot_nginx/tests/http_01_test.py0000644000076600000240000000747613261244762023214 0ustar bmwstaff00000000000000"""Tests for certbot_nginx.http_01""" import unittest import shutil import mock import six from acme import challenges from certbot import achallenges from certbot.plugins import common_test from certbot.tests import acme_util from certbot_nginx.tests import util class HttpPerformTest(util.NginxTest): """Test the NginxHttp01 challenge.""" account_key = common_test.AUTH_KEY achalls = [ achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01(token=b"kNdwjwOeX0I_A8DXt9Msmg"), "pending"), domain="www.example.com", account_key=account_key), achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.HTTP01( token=b"\xba\xa9\xda?= 2 and x[0] == ['server'], lambda x, y: srv.append((x[1], y))) # Find 'include' statements in server blocks and append their trees for i, (server, path) in enumerate(servers[filename]): new_server = self._get_included_directives(server) servers[filename][i] = (new_server, path) return servers def get_vhosts(self): # pylint: disable=cell-var-from-loop """Gets list of all 'virtual hosts' found in Nginx configuration. Technically this is a misnomer because Nginx does not have virtual hosts, it has 'server blocks'. :returns: List of :class:`~certbot_nginx.obj.VirtualHost` objects found in configuration :rtype: list """ enabled = True # We only look at enabled vhosts for now servers = self._get_raw_servers() vhosts = [] for filename in servers: for server, path in servers[filename]: # Parse the server block into a VirtualHost object parsed_server = _parse_server_raw(server) vhost = obj.VirtualHost(filename, parsed_server['addrs'], parsed_server['ssl'], enabled, parsed_server['names'], server, path) vhosts.append(vhost) self._update_vhosts_addrs_ssl(vhosts) return vhosts def _update_vhosts_addrs_ssl(self, vhosts): """Update a list of raw parsed vhosts to include global address sslishness """ addr_to_ssl = self._build_addr_to_ssl() for vhost in vhosts: for addr in vhost.addrs: addr.ssl = addr_to_ssl[addr.normalized_tuple()] if addr.ssl: vhost.ssl = True def _get_included_directives(self, block): """Returns array with the "include" directives expanded out by concatenating the contents of the included file to the block. :param list block: :rtype: list """ result = copy.deepcopy(block) # Copy the list to keep self.parsed idempotent for directive in block: if _is_include_directive(directive): included_files = glob.glob( self.abs_path(directive[1])) for incl in included_files: try: result.extend(self.parsed[incl]) except KeyError: pass return result def _parse_files(self, filepath, override=False): """Parse files from a glob :param str filepath: Nginx config file path :param bool override: Whether to parse a file that has been parsed :returns: list of parsed tree structures :rtype: list """ files = glob.glob(filepath) # nginx on unix calls glob(3) for this # XXX Windows nginx uses FindFirstFile, and # should have a narrower call here trees = [] for item in files: if item in self.parsed and not override: continue try: with open(item) as _file: parsed = nginxparser.load(_file) self.parsed[item] = parsed trees.append(parsed) except IOError: logger.warning("Could not open file: %s", item) except pyparsing.ParseException as err: logger.debug("Could not parse file: %s due to %s", item, err) return trees def _find_config_root(self): """Return the Nginx Configuration Root file.""" location = ['nginx.conf'] for name in location: if os.path.isfile(os.path.join(self.root, name)): return os.path.join(self.root, name) raise errors.NoInstallationError( "Could not find configuration root") def filedump(self, ext='tmp', lazy=True): """Dumps parsed configurations into files. :param str ext: The file extension to use for the dumped files. If empty, this overrides the existing conf files. :param bool lazy: Only write files that have been modified """ # Best-effort atomicity is enforced above us by reverter.py for filename in self.parsed: tree = self.parsed[filename] if ext: filename = filename + os.path.extsep + ext try: if lazy and not tree.is_dirty(): continue out = nginxparser.dumps(tree) logger.debug('Writing nginx conf tree to %s:\n%s', filename, out) with open(filename, 'w') as _file: _file.write(out) except IOError: logger.error("Could not open file for writing: %s", filename) def parse_server(self, server): """Parses a list of server directives, accounting for global address sslishness. :param list server: list of directives in a server block :rtype: dict """ addr_to_ssl = self._build_addr_to_ssl() parsed_server = _parse_server_raw(server) _apply_global_addr_ssl(addr_to_ssl, parsed_server) return parsed_server def has_ssl_on_directive(self, vhost): """Does vhost have ssl on for all ports? :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost in question :returns: True if 'ssl on' directive is included :rtype: bool """ server = vhost.raw for directive in server: if not directive: continue elif _is_ssl_on_directive(directive): return True return False def add_server_directives(self, vhost, directives, insert_at_top=False): """Add directives to the server block identified by vhost. This method modifies vhost to be fully consistent with the new directives. ..note :: It's an error to try and add a nonrepeatable directive that already exists in the config block with a conflicting value. ..todo :: Doesn't match server blocks whose server_name directives are split across multiple conf files. :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost whose information we use to match on :param list directives: The directives to add :param bool insert_at_top: True if the directives need to be inserted at the top of the server block instead of the bottom """ self._modify_server_directives(vhost, functools.partial(_add_directives, directives, insert_at_top)) def update_or_add_server_directives(self, vhost, directives, insert_at_top=False): """Add or replace directives in the server block identified by vhost. This method modifies vhost to be fully consistent with the new directives. ..note :: When a directive with the same name already exists in the config block, the first instance will be replaced. Otherwise, the directive will be appended/prepended to the config block as in add_server_directives. ..todo :: Doesn't match server blocks whose server_name directives are split across multiple conf files. :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost whose information we use to match on :param list directives: The directives to add :param bool insert_at_top: True if the directives need to be inserted at the top of the server block instead of the bottom """ self._modify_server_directives(vhost, functools.partial(_update_or_add_directives, directives, insert_at_top)) def remove_server_directives(self, vhost, directive_name, match_func=None): """Remove all directives of type directive_name. :param :class:`~certbot_nginx.obj.VirtualHost` vhost: The vhost to remove directives from :param string directive_name: The directive type to remove :param callable match_func: Function of the directive that returns true for directives to be deleted. """ self._modify_server_directives(vhost, functools.partial(_remove_directives, directive_name, match_func)) def _update_vhost_based_on_new_directives(self, vhost, directives_list): new_server = self._get_included_directives(directives_list) parsed_server = self.parse_server(new_server) vhost.addrs = parsed_server['addrs'] vhost.ssl = parsed_server['ssl'] vhost.names = parsed_server['names'] vhost.raw = new_server def _modify_server_directives(self, vhost, block_func): filename = vhost.filep try: result = self.parsed[filename] for index in vhost.path: result = result[index] if not isinstance(result, list) or len(result) != 2: raise errors.MisconfigurationError("Not a server block.") result = result[1] block_func(result) self._update_vhost_based_on_new_directives(vhost, result) except errors.MisconfigurationError as err: raise errors.MisconfigurationError("Problem in %s: %s" % (filename, str(err))) def duplicate_vhost(self, vhost_template, remove_singleton_listen_params=False, only_directives=None): """Duplicate the vhost in the configuration files. :param :class:`~certbot_nginx.obj.VirtualHost` vhost_template: The vhost whose information we copy :param bool remove_singleton_listen_params: If we should remove parameters from listen directives in the block that can only be used once per address :param list only_directives: If it exists, only duplicate the named directives. Only looks at first level of depth; does not expand includes. :returns: A vhost object for the newly created vhost :rtype: :class:`~certbot_nginx.obj.VirtualHost` """ # TODO: https://github.com/certbot/certbot/issues/5185 # put it in the same file as the template, at the same level new_vhost = copy.deepcopy(vhost_template) enclosing_block = self.parsed[vhost_template.filep] for index in vhost_template.path[:-1]: enclosing_block = enclosing_block[index] raw_in_parsed = copy.deepcopy(enclosing_block[vhost_template.path[-1]]) if only_directives is not None: new_directives = nginxparser.UnspacedList([]) for directive in raw_in_parsed[1]: if len(directive) > 0 and directive[0] in only_directives: new_directives.append(directive) raw_in_parsed[1] = new_directives self._update_vhost_based_on_new_directives(new_vhost, new_directives) enclosing_block.append(raw_in_parsed) new_vhost.path[-1] = len(enclosing_block) - 1 if remove_singleton_listen_params: for addr in new_vhost.addrs: addr.default = False addr.ipv6only = False for directive in enclosing_block[new_vhost.path[-1]][1]: if len(directive) > 0 and directive[0] == 'listen': if 'default_server' in directive: del directive[directive.index('default_server')] if 'default' in directive: del directive[directive.index('default')] if 'ipv6only=on' in directive: del directive[directive.index('ipv6only=on')] return new_vhost def _parse_ssl_options(ssl_options): if ssl_options is not None: try: with open(ssl_options) as _file: return nginxparser.load(_file) except IOError: logger.warn("Missing NGINX TLS options file: %s", ssl_options) except pyparsing.ParseBaseException as err: logger.debug("Could not parse file: %s due to %s", ssl_options, err) return [] def _do_for_subarray(entry, condition, func, path=None): """Executes a function for a subarray of a nested array if it matches the given condition. :param list entry: The list to iterate over :param function condition: Returns true iff func should be executed on item :param function func: The function to call for each matching item """ if path is None: path = [] if isinstance(entry, list): if condition(entry): func(entry, path) else: for index, item in enumerate(entry): _do_for_subarray(item, condition, func, path + [index]) def get_best_match(target_name, names): """Finds the best match for target_name out of names using the Nginx name-matching rules (exact > longest wildcard starting with * > longest wildcard ending with * > regex). :param str target_name: The name to match :param set names: The candidate server names :returns: Tuple of (type of match, the name that matched) :rtype: tuple """ exact = [] wildcard_start = [] wildcard_end = [] regex = [] for name in names: if _exact_match(target_name, name): exact.append(name) elif _wildcard_match(target_name, name, True): wildcard_start.append(name) elif _wildcard_match(target_name, name, False): wildcard_end.append(name) elif _regex_match(target_name, name): regex.append(name) if len(exact) > 0: # There can be more than one exact match; e.g. eff.org, .eff.org match = min(exact, key=len) return ('exact', match) if len(wildcard_start) > 0: # Return the longest wildcard match = max(wildcard_start, key=len) return ('wildcard_start', match) if len(wildcard_end) > 0: # Return the longest wildcard match = max(wildcard_end, key=len) return ('wildcard_end', match) if len(regex) > 0: # Just return the first one for now match = regex[0] return ('regex', match) return (None, None) def _exact_match(target_name, name): return target_name == name or '.' + target_name == name def _wildcard_match(target_name, name, start): # Degenerate case if name == '*': return True parts = target_name.split('.') match_parts = name.split('.') # If the domain ends in a wildcard, do the match procedure in reverse if not start: parts.reverse() match_parts.reverse() # The first part must be a wildcard or blank, e.g. '.eff.org' first = match_parts.pop(0) if first != '*' and first != '': return False target_name = '.'.join(parts) name = '.'.join(match_parts) # Ex: www.eff.org matches *.eff.org, eff.org does not match *.eff.org return target_name.endswith('.' + name) def _regex_match(target_name, name): # Must start with a tilde if len(name) < 2 or name[0] != '~': return False # After tilde is a perl-compatible regex try: regex = re.compile(name[1:]) if re.match(regex, target_name): return True else: return False except re.error: # pragma: no cover # perl-compatible regexes are sometimes not recognized by python return False def _is_include_directive(entry): """Checks if an nginx parsed entry is an 'include' directive. :param list entry: the parsed entry :returns: Whether it's an 'include' directive :rtype: bool """ return (isinstance(entry, list) and len(entry) == 2 and entry[0] == 'include' and isinstance(entry[1], six.string_types)) def _is_ssl_on_directive(entry): """Checks if an nginx parsed entry is an 'ssl on' directive. :param list entry: the parsed entry :returns: Whether it's an 'ssl on' directive :rtype: bool """ return (isinstance(entry, list) and len(entry) == 2 and entry[0] == 'ssl' and entry[1] == 'on') def _add_directives(directives, insert_at_top, block): """Adds directives to a config block.""" for directive in directives: _add_directive(block, directive, insert_at_top) if block and '\n' not in block[-1]: # could be " \n " or ["\n"] ! block.append(nginxparser.UnspacedList('\n')) def _update_or_add_directives(directives, insert_at_top, block): """Adds or replaces directives in a config block.""" for directive in directives: _update_or_add_directive(block, directive, insert_at_top) if block and '\n' not in block[-1]: # could be " \n " or ["\n"] ! block.append(nginxparser.UnspacedList('\n')) INCLUDE = 'include' REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE, 'rewrite']) COMMENT = ' managed by Certbot' COMMENT_BLOCK = [' ', '#', COMMENT] def comment_directive(block, location): """Add a ``#managed by Certbot`` comment to the end of the line at location. :param list block: The block containing the directive to be commented :param int location: The location within ``block`` of the directive to be commented """ next_entry = block[location + 1] if location + 1 < len(block) else None if isinstance(next_entry, list) and next_entry: if len(next_entry) >= 2 and next_entry[-2] == "#" and COMMENT in next_entry[-1]: return elif isinstance(next_entry, nginxparser.UnspacedList): next_entry = next_entry.spaced[0] else: next_entry = next_entry[0] block.insert(location + 1, COMMENT_BLOCK[:]) if next_entry is not None and "\n" not in next_entry: block.insert(location + 2, '\n') def _comment_out_directive(block, location, include_location): """Comment out the line at location, with a note of explanation.""" comment_message = ' duplicated in {0}'.format(include_location) # add the end comment # create a dumpable object out of block[location] (so it includes the ;) directive = block[location] new_dir_block = nginxparser.UnspacedList([]) # just a wrapper new_dir_block.append(directive) dumped = nginxparser.dumps(new_dir_block) commented = dumped + ' #' + comment_message # add the comment directly to the one-line string new_dir = nginxparser.loads(commented) # reload into UnspacedList # add the beginning comment insert_location = 0 if new_dir[0].spaced[0] != new_dir[0][0]: # if there's whitespace at the beginning insert_location = 1 new_dir[0].spaced.insert(insert_location, "# ") # comment out the line new_dir[0].spaced.append(";") # directly add in the ;, because now dumping won't work properly dumped = nginxparser.dumps(new_dir) new_dir = nginxparser.loads(dumped) # reload into an UnspacedList block[location] = new_dir[0] # set the now-single-line-comment directive back in place def _find_location(block, directive_name, match_func=None): """Finds the index of the first instance of directive_name in block. If no line exists, use None.""" return next((index for index, line in enumerate(block) \ if line and line[0] == directive_name and (match_func is None or match_func(line))), None) def _is_whitespace_or_comment(directive): """Is this directive either a whitespace or comment directive?""" return len(directive) == 0 or directive[0] == '#' def _add_directive(block, directive, insert_at_top): if not isinstance(directive, nginxparser.UnspacedList): directive = nginxparser.UnspacedList(directive) if _is_whitespace_or_comment(directive): # whitespace or comment block.append(directive) return location = _find_location(block, directive[0]) # Append or prepend directive. Fail if the name is not a repeatable directive name, # and there is already a copy of that directive with a different value # in the config file. # handle flat include files directive_name = directive[0] def can_append(loc, dir_name): """ Can we append this directive to the block? """ return loc is None or (isinstance(dir_name, six.string_types) and dir_name in REPEATABLE_DIRECTIVES) err_fmt = 'tried to insert directive "{0}" but found conflicting "{1}".' # Give a better error message about the specific directive than Nginx's "fail to restart" if directive_name == INCLUDE: # in theory, we might want to do this recursively, but in practice, that's really not # necessary because we know what file we're talking about (and if we don't recurse, we # just give a worse error message) included_directives = _parse_ssl_options(directive[1]) for included_directive in included_directives: included_dir_loc = _find_location(block, included_directive[0]) included_dir_name = included_directive[0] if not _is_whitespace_or_comment(included_directive) \ and not can_append(included_dir_loc, included_dir_name): if block[included_dir_loc] != included_directive: raise errors.MisconfigurationError(err_fmt.format(included_directive, block[included_dir_loc])) else: _comment_out_directive(block, included_dir_loc, directive[1]) if can_append(location, directive_name): if insert_at_top: # Add a newline so the comment doesn't comment # out existing directives block.insert(0, nginxparser.UnspacedList('\n')) block.insert(0, directive) comment_directive(block, 0) else: block.append(directive) comment_directive(block, len(block) - 1) elif block[location] != directive: raise errors.MisconfigurationError(err_fmt.format(directive, block[location])) def _update_directive(block, directive, location): block[location] = directive comment_directive(block, location) def _update_or_add_directive(block, directive, insert_at_top): if not isinstance(directive, nginxparser.UnspacedList): directive = nginxparser.UnspacedList(directive) if _is_whitespace_or_comment(directive): # whitespace or comment block.append(directive) return location = _find_location(block, directive[0]) # we can update directive if location is not None: _update_directive(block, directive, location) return _add_directive(block, directive, insert_at_top) def _is_certbot_comment(directive): return '#' in directive and COMMENT in directive def _remove_directives(directive_name, match_func, block): """Removes directives of name directive_name from a config block if match_func matches. """ while True: location = _find_location(block, directive_name, match_func=match_func) if location is None: return # if the directive was made by us, remove the comment following if location + 1 < len(block) and _is_certbot_comment(block[location + 1]): del block[location + 1] del block[location] def _apply_global_addr_ssl(addr_to_ssl, parsed_server): """Apply global sslishness information to the parsed server block """ for addr in parsed_server['addrs']: addr.ssl = addr_to_ssl[addr.normalized_tuple()] if addr.ssl: parsed_server['ssl'] = True def _parse_server_raw(server): """Parses a list of server directives. :param list server: list of directives in a server block :rtype: dict """ parsed_server = {'addrs': set(), 'ssl': False, 'names': set()} apply_ssl_to_all_addrs = False for directive in server: if not directive: continue if directive[0] == 'listen': addr = obj.Addr.fromstring(" ".join(directive[1:])) if addr: parsed_server['addrs'].add(addr) if addr.ssl: parsed_server['ssl'] = True elif directive[0] == 'server_name': parsed_server['names'].update(x.strip('"\'') for x in directive[1:]) elif _is_ssl_on_directive(directive): parsed_server['ssl'] = True apply_ssl_to_all_addrs = True if apply_ssl_to_all_addrs: for addr in parsed_server['addrs']: addr.ssl = True return parsed_server certbot-nginx-0.23.0/certbot_nginx/obj.py0000644000076600000240000002233413261244762020274 0ustar bmwstaff00000000000000"""Module contains classes used by the Nginx Configurator.""" import re import six from certbot.plugins import common ADD_HEADER_DIRECTIVE = 'add_header' class Addr(common.Addr): r"""Represents an Nginx address, i.e. what comes after the 'listen' directive. According to the `documentation`_, this may be address[:port], port, or unix:path. The latter is ignored here. The default value if no directive is specified is \*:80 (superuser) or \*:8000 (otherwise). If no port is specified, the default is 80. If no address is specified, listen on all addresses. .. _documentation: http://nginx.org/en/docs/http/ngx_http_core_module.html#listen .. todo:: Old-style nginx configs define SSL vhosts in a separate block instead of using 'ssl' in the listen directive. :param str addr: addr part of vhost address, may be hostname, IPv4, IPv6, "", or "\*" :param str port: port number or "\*" or "" :param bool ssl: Whether the directive includes 'ssl' :param bool default: Whether the directive includes 'default_server' :param bool default: Whether this is an IPv6 address :param bool ipv6only: Whether the directive includes 'ipv6only=on' """ UNSPECIFIED_IPV4_ADDRESSES = ('', '*', '0.0.0.0') CANONICAL_UNSPECIFIED_ADDRESS = UNSPECIFIED_IPV4_ADDRESSES[0] def __init__(self, host, port, ssl, default, ipv6, ipv6only): # pylint: disable=too-many-arguments super(Addr, self).__init__((host, port)) self.ssl = ssl self.default = default self.ipv6 = ipv6 self.ipv6only = ipv6only self.unspecified_address = host in self.UNSPECIFIED_IPV4_ADDRESSES @classmethod def fromstring(cls, str_addr): """Initialize Addr from string.""" parts = str_addr.split(' ') ssl = False default = False ipv6 = False ipv6only = False host = '' port = '' # The first part must be the address addr = parts.pop(0) # Ignore UNIX-domain sockets if addr.startswith('unix:'): return None # IPv6 check ipv6_match = re.match(r'\[.*\]', addr) if ipv6_match: ipv6 = True # IPv6 handling host = ipv6_match.group() # The rest of the addr string will be the port, if any port = addr[ipv6_match.end()+1:] else: # IPv4 handling tup = addr.partition(':') if re.match(r'^\d+$', tup[0]): # This is a bare port, not a hostname. E.g. listen 80 host = '' port = tup[0] else: # This is a host-port tuple. E.g. listen 127.0.0.1:* host = tup[0] port = tup[2] # The rest of the parts are options; we only care about ssl and default while len(parts) > 0: nextpart = parts.pop() if nextpart == 'ssl': ssl = True elif nextpart == 'default_server': default = True elif nextpart == 'default': default = True elif nextpart == "ipv6only=on": ipv6only = True return cls(host, port, ssl, default, ipv6, ipv6only) def to_string(self, include_default=True): """Return string representation of Addr""" parts = '' if self.tup[0] and self.tup[1]: parts = "%s:%s" % self.tup elif self.tup[0]: parts = self.tup[0] else: parts = self.tup[1] if self.default and include_default: parts += ' default_server' if self.ssl: parts += ' ssl' return parts def __str__(self): return self.to_string() def __repr__(self): return "Addr(" + self.__str__() + ")" def __hash__(self): # Python 3 requires explicit overridden for __hash__ # See certbot-apache/certbot_apache/obj.py for more information return super(Addr, self).__hash__() def super_eq(self, other): """Check ip/port equality, with IPv6 support. """ # If both addresses got an unspecified address, then make sure the # host representation in each match when doing the comparison. if self.unspecified_address and other.unspecified_address: return common.Addr((self.CANONICAL_UNSPECIFIED_ADDRESS, self.tup[1]), self.ipv6) == \ common.Addr((other.CANONICAL_UNSPECIFIED_ADDRESS, other.tup[1]), other.ipv6) return super(Addr, self).__eq__(other) def __eq__(self, other): if isinstance(other, self.__class__): return (self.super_eq(other) and self.ssl == other.ssl and self.default == other.default) return False class VirtualHost(object): # pylint: disable=too-few-public-methods """Represents an Nginx Virtualhost. :ivar str filep: file path of VH :ivar set addrs: Virtual Host addresses (:class:`set` of :class:`Addr`) :ivar set names: Server names/aliases of vhost (:class:`list` of :class:`str`) :ivar list raw: The raw form of the parsed server block :ivar bool ssl: SSLEngine on in vhost :ivar bool enabled: Virtual host is enabled :ivar list path: The indices into the parsed file used to access the server block defining the vhost """ def __init__(self, filep, addrs, ssl, enabled, names, raw, path): # pylint: disable=too-many-arguments """Initialize a VH.""" self.filep = filep self.addrs = addrs self.names = names self.ssl = ssl self.enabled = enabled self.raw = raw self.path = path def __str__(self): addr_str = ", ".join(str(addr) for addr in sorted(self.addrs, key=str)) # names might be a set, and it has different representations in Python # 2 and 3. Force it to be a list here for consistent outputs return ("file: %s\n" "addrs: %s\n" "names: %s\n" "ssl: %s\n" "enabled: %s" % (self.filep, addr_str, list(self.names), self.ssl, self.enabled)) def __repr__(self): return "VirtualHost(" + self.__str__().replace("\n", ", ") + ")\n" def __eq__(self, other): if isinstance(other, self.__class__): return (self.filep == other.filep and sorted(self.addrs, key=str) == sorted(other.addrs, key=str) and self.names == other.names and self.ssl == other.ssl and self.enabled == other.enabled and self.path == other.path) return False def __hash__(self): return hash((self.filep, tuple(self.path), tuple(self.addrs), tuple(self.names), self.ssl, self.enabled)) def has_header(self, header_name): """Determine if this server block has a particular header set. :param str header_name: The name of the header to check for, e.g. 'Strict-Transport-Security' """ found = _find_directive(self.raw, ADD_HEADER_DIRECTIVE, header_name) return found is not None def contains_list(self, test): """Determine if raw server block contains test list at top level """ for i in six.moves.range(0, len(self.raw) - len(test) + 1): if self.raw[i:i + len(test)] == test: return True return False def ipv6_enabled(self): """Return true if one or more of the listen directives in vhost supports IPv6""" for a in self.addrs: if a.ipv6: return True def ipv4_enabled(self): """Return true if one or more of the listen directives in vhost are IPv4 only""" if self.addrs is None or len(self.addrs) == 0: return True for a in self.addrs: if not a.ipv6: return True def display_repr(self): """Return a representation of VHost to be used in dialog""" return ( "File: {filename}\n" "Addresses: {addrs}\n" "Names: {names}\n" "HTTPS: {https}\n".format( filename=self.filep, addrs=", ".join(str(addr) for addr in self.addrs), names=", ".join(self.names), https="Yes" if self.ssl else "No")) def _find_directive(directives, directive_name, match_content=None): """Find a directive of type directive_name in directives. If match_content is given, Searches for `match_content` in the directive arguments. """ if not directives or isinstance(directives, six.string_types) or len(directives) == 0: return None # If match_content is None, just match on directive type. Otherwise, match on # both directive type -and- the content! if directives[0] == directive_name and \ (match_content is None or match_content in directives): return directives matches = (_find_directive(line, directive_name, match_content) for line in directives) return next((m for m in matches if m is not None), None) certbot-nginx-0.23.0/certbot_nginx/configurator.py0000644000076600000240000012633713261244762022234 0ustar bmwstaff00000000000000"""Nginx Configuration""" import logging import os import re import socket import subprocess import tempfile import time import OpenSSL import six import zope.interface from acme import challenges from acme import crypto_util as acme_crypto_util from certbot import constants as core_constants from certbot import crypto_util from certbot import errors from certbot import interfaces from certbot import util from certbot.plugins import common from certbot_nginx import constants from certbot_nginx import display_ops from certbot_nginx import nginxparser from certbot_nginx import parser from certbot_nginx import tls_sni_01 from certbot_nginx import http_01 logger = logging.getLogger(__name__) @zope.interface.implementer(interfaces.IAuthenticator, interfaces.IInstaller) @zope.interface.provider(interfaces.IPluginFactory) class NginxConfigurator(common.Installer): # pylint: disable=too-many-instance-attributes,too-many-public-methods """Nginx configurator. .. todo:: Add proper support for comments in the config. Currently, config files modified by the configurator will lose all their comments. :ivar config: Configuration. :type config: :class:`~certbot.interfaces.IConfig` :ivar parser: Handles low level parsing :type parser: :class:`~certbot_nginx.parser` :ivar str save_notes: Human-readable config change notes :ivar reverter: saves and reverts checkpoints :type reverter: :class:`certbot.reverter.Reverter` :ivar tup version: version of Nginx """ description = "Nginx Web Server plugin - Alpha" DEFAULT_LISTEN_PORT = '80' # SSL directives that Certbot can add when installing a new certificate. SSL_DIRECTIVES = ['ssl_certificate', 'ssl_certificate_key', 'ssl_dhparam'] @classmethod def add_parser_arguments(cls, add): add("server-root", default=constants.CLI_DEFAULTS["server_root"], help="Nginx server root directory.") add("ctl", default=constants.CLI_DEFAULTS["ctl"], help="Path to the " "'nginx' binary, used for 'configtest' and retrieving nginx " "version number.") @property def nginx_conf(self): """Nginx config file path.""" return os.path.join(self.conf("server_root"), "nginx.conf") def __init__(self, *args, **kwargs): """Initialize an Nginx Configurator. :param tup version: version of Nginx as a tuple (1, 4, 7) (used mostly for unittesting) """ version = kwargs.pop("version", None) super(NginxConfigurator, self).__init__(*args, **kwargs) # Verify that all directories and files exist with proper permissions self._verify_setup() # Files to save self.save_notes = "" # For creating new vhosts if no names match self.new_vhost = None # List of vhosts configured per wildcard domain on this run. # used by deploy_cert() and enhance() self._wildcard_vhosts = {} self._wildcard_redirect_vhosts = {} # Add number of outstanding challenges self._chall_out = 0 # These will be set in the prepare function self.parser = None self.version = version self._enhance_func = {"redirect": self._enable_redirect, "ensure-http-header": self._set_http_header, "staple-ocsp": self._enable_ocsp_stapling} self.reverter.recovery_routine() @property def mod_ssl_conf(self): """Full absolute path to SSL configuration file.""" return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST) @property def updated_mod_ssl_conf_digest(self): """Full absolute path to digest of updated SSL configuration file.""" return os.path.join(self.config.config_dir, constants.UPDATED_MOD_SSL_CONF_DIGEST) # This is called in determine_authenticator and determine_installer def prepare(self): """Prepare the authenticator/installer. :raises .errors.NoInstallationError: If Nginx ctl cannot be found :raises .errors.MisconfigurationError: If Nginx is misconfigured """ # Verify Nginx is installed if not util.exe_exists(self.conf('ctl')): raise errors.NoInstallationError # Make sure configuration is valid self.config_test() self.parser = parser.NginxParser(self.conf('server-root')) install_ssl_options_conf(self.mod_ssl_conf, self.updated_mod_ssl_conf_digest) self.install_ssl_dhparams() # Set Version if self.version is None: self.version = self.get_version() # Prevent two Nginx plugins from modifying a config at once try: util.lock_dir_until_exit(self.conf('server-root')) except (OSError, errors.LockError): logger.debug('Encountered error:', exc_info=True) raise errors.PluginError( 'Unable to lock %s', self.conf('server-root')) # Entry point in main.py for installing cert def deploy_cert(self, domain, cert_path, key_path, chain_path=None, fullchain_path=None): # pylint: disable=unused-argument """Deploys certificate to specified virtual host. .. note:: Aborts if the vhost is missing ssl_certificate or ssl_certificate_key. .. note:: This doesn't save the config files! :raises errors.PluginError: When unable to deploy certificate due to a lack of directives or configuration """ if not fullchain_path: raise errors.PluginError( "The nginx plugin currently requires --fullchain-path to " "install a cert.") vhosts = self.choose_vhosts(domain, create_if_no_match=True) for vhost in vhosts: self._deploy_cert(vhost, cert_path, key_path, chain_path, fullchain_path) def _deploy_cert(self, vhost, cert_path, key_path, chain_path, fullchain_path): # pylint: disable=unused-argument """ Helper function for deploy_cert() that handles the actual deployment this exists because we might want to do multiple deployments per domain originally passed for deploy_cert(). This is especially true with wildcard certificates """ cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path], ['\n ', 'ssl_certificate_key', ' ', key_path]] self.parser.update_or_add_server_directives(vhost, cert_directives) logger.info("Deploying Certificate to VirtualHost %s", vhost.filep) self.save_notes += ("Changed vhost at %s with addresses of %s\n" % (vhost.filep, ", ".join(str(addr) for addr in vhost.addrs))) self.save_notes += "\tssl_certificate %s\n" % fullchain_path self.save_notes += "\tssl_certificate_key %s\n" % key_path def _choose_vhosts_wildcard(self, domain, prefer_ssl, no_ssl_filter_port=None): """Prompts user to choose vhosts to install a wildcard certificate for""" if prefer_ssl: vhosts_cache = self._wildcard_vhosts preference_test = lambda x: x.ssl else: vhosts_cache = self._wildcard_redirect_vhosts preference_test = lambda x: not x.ssl # Caching! if domain in vhosts_cache: # Vhosts for a wildcard domain were already selected return vhosts_cache[domain] # Get all vhosts whether or not they are covered by the wildcard domain vhosts = self.parser.get_vhosts() # Go through the vhosts, making sure that we cover all the names # present, but preferring the SSL or non-SSL vhosts filtered_vhosts = {} for vhost in vhosts: # Ensure we're listening non-sslishly on no_ssl_filter_port if no_ssl_filter_port is not None: if not self._vhost_listening_on_port_no_ssl(vhost, no_ssl_filter_port): continue for name in vhost.names: if preference_test(vhost): # Prefer either SSL or non-SSL vhosts filtered_vhosts[name] = vhost elif name not in filtered_vhosts: # Add if not in list previously filtered_vhosts[name] = vhost # Only unique VHost objects dialog_input = set([vhost for vhost in filtered_vhosts.values()]) # Ask the user which of names to enable, expect list of names back return_vhosts = display_ops.select_vhost_multiple(list(dialog_input)) for vhost in return_vhosts: if domain not in vhosts_cache: vhosts_cache[domain] = [] vhosts_cache[domain].append(vhost) return return_vhosts ####################### # Vhost parsing methods ####################### def _choose_vhost_single(self, target_name): matches = self._get_ranked_matches(target_name) vhosts = [x for x in [self._select_best_name_match(matches)] if x is not None] return vhosts def choose_vhosts(self, target_name, create_if_no_match=False): """Chooses a virtual host based on the given domain name. .. note:: This makes the vhost SSL-enabled if it isn't already. Follows Nginx's server block selection rules preferring blocks that are already SSL. .. todo:: This should maybe return list if no obvious answer is presented. .. todo:: The special name "$hostname" corresponds to the machine's hostname. Currently we just ignore this. :param str target_name: domain name :param bool create_if_no_match: If we should create a new vhost from default when there is no match found. If we can't choose a default, raise a MisconfigurationError. :returns: ssl vhosts associated with name :rtype: list of :class:`~certbot_nginx.obj.VirtualHost` """ if util.is_wildcard_domain(target_name): # Ask user which VHosts to support. vhosts = self._choose_vhosts_wildcard(target_name, prefer_ssl=True) else: vhosts = self._choose_vhost_single(target_name) if not vhosts: if create_if_no_match: # result will not be [None] because it errors on failure vhosts = [self._vhost_from_duplicated_default(target_name)] else: # No matches. Raise a misconfiguration error. raise errors.MisconfigurationError( ("Cannot find a VirtualHost matching domain %s. " "In order for Certbot to correctly perform the challenge " "please add a corresponding server_name directive to your " "nginx configuration: " "https://nginx.org/en/docs/http/server_names.html") % (target_name)) # Note: if we are enhancing with ocsp, vhost should already be ssl. for vhost in vhosts: if not vhost.ssl: self._make_server_ssl(vhost) return vhosts def ipv6_info(self, port): """Returns tuple of booleans (ipv6_active, ipv6only_present) ipv6_active is true if any server block listens ipv6 address in any port ipv6only_present is true if ipv6only=on option exists in any server block ipv6 listen directive for the specified port. :param str port: Port to check ipv6only=on directive for :returns: Tuple containing information if IPv6 is enabled in the global configuration, and existence of ipv6only directive for specified port :rtype: tuple of type (bool, bool) """ # port should be a string, but it's easy to mess up, so let's # make sure it is one port = str(port) vhosts = self.parser.get_vhosts() ipv6_active = False ipv6only_present = False for vh in vhosts: for addr in vh.addrs: if addr.ipv6: ipv6_active = True if addr.ipv6only and addr.get_port() == port: ipv6only_present = True return (ipv6_active, ipv6only_present) def _vhost_from_duplicated_default(self, domain, port=None): if self.new_vhost is None: default_vhost = self._get_default_vhost(port) self.new_vhost = self.parser.duplicate_vhost(default_vhost, remove_singleton_listen_params=True) self.new_vhost.names = set() self._add_server_name_to_vhost(self.new_vhost, domain) return self.new_vhost def _add_server_name_to_vhost(self, vhost, domain): vhost.names.add(domain) name_block = [['\n ', 'server_name']] for name in vhost.names: name_block[0].append(' ') name_block[0].append(name) self.parser.update_or_add_server_directives(vhost, name_block) def _get_default_vhost(self, port): vhost_list = self.parser.get_vhosts() # if one has default_server set, return that one default_vhosts = [] for vhost in vhost_list: for addr in vhost.addrs: if addr.default: if port is None or self._port_matches(port, addr.get_port()): default_vhosts.append(vhost) break if len(default_vhosts) == 1: return default_vhosts[0] # TODO: present a list of vhosts for user to choose from raise errors.MisconfigurationError("Could not automatically find a matching server" " block. Set the `server_name` directive to use the Nginx installer.") def _get_ranked_matches(self, target_name): """Returns a ranked list of vhosts that match target_name. The ranking gives preference to SSL vhosts. :param str target_name: The name to match :returns: list of dicts containing the vhost, the matching name, and the numerical rank :rtype: list """ vhost_list = self.parser.get_vhosts() return self._rank_matches_by_name_and_ssl(vhost_list, target_name) def _select_best_name_match(self, matches): """Returns the best name match of a ranked list of vhosts. :param list matches: list of dicts containing the vhost, the matching name, and the numerical rank :returns: the most matching vhost :rtype: :class:`~certbot_nginx.obj.VirtualHost` """ if not matches: return None elif matches[0]['rank'] in six.moves.range(2, 6): # Wildcard match - need to find the longest one rank = matches[0]['rank'] wildcards = [x for x in matches if x['rank'] == rank] return max(wildcards, key=lambda x: len(x['name']))['vhost'] else: # Exact or regex match return matches[0]['vhost'] def _rank_matches_by_name_and_ssl(self, vhost_list, target_name): """Returns a ranked list of vhosts from vhost_list that match target_name. The ranking gives preference to SSL vhosts. :param list vhost_list: list of vhosts to filter and rank :param str target_name: The name to match :returns: list of dicts containing the vhost, the matching name, and the numerical rank :rtype: list """ # Nginx chooses a matching server name for a request with precedence: # 1. exact name match # 2. longest wildcard name starting with * # 3. longest wildcard name ending with * # 4. first matching regex in order of appearance in the file matches = [] for vhost in vhost_list: name_type, name = parser.get_best_match(target_name, vhost.names) if name_type == 'exact': matches.append({'vhost': vhost, 'name': name, 'rank': 0 if vhost.ssl else 1}) elif name_type == 'wildcard_start': matches.append({'vhost': vhost, 'name': name, 'rank': 2 if vhost.ssl else 3}) elif name_type == 'wildcard_end': matches.append({'vhost': vhost, 'name': name, 'rank': 4 if vhost.ssl else 5}) elif name_type == 'regex': matches.append({'vhost': vhost, 'name': name, 'rank': 6 if vhost.ssl else 7}) return sorted(matches, key=lambda x: x['rank']) def choose_redirect_vhosts(self, target_name, port, create_if_no_match=False): """Chooses a single virtual host for redirect enhancement. Chooses the vhost most closely matching target_name that is listening to port without using ssl. .. todo:: This should maybe return list if no obvious answer is presented. .. todo:: The special name "$hostname" corresponds to the machine's hostname. Currently we just ignore this. :param str target_name: domain name :param str port: port number :param bool create_if_no_match: If we should create a new vhost from default when there is no match found. If we can't choose a default, raise a MisconfigurationError. :returns: vhosts associated with name :rtype: list of :class:`~certbot_nginx.obj.VirtualHost` """ if util.is_wildcard_domain(target_name): # Ask user which VHosts to enhance. vhosts = self._choose_vhosts_wildcard(target_name, prefer_ssl=False, no_ssl_filter_port=port) else: matches = self._get_redirect_ranked_matches(target_name, port) vhosts = [x for x in [self._select_best_name_match(matches)]if x is not None] if not vhosts and create_if_no_match: vhosts = [self._vhost_from_duplicated_default(target_name, port=port)] return vhosts def _port_matches(self, test_port, matching_port): # test_port is a number, matching is a number or "" or None if matching_port == "" or matching_port is None: # if no port is specified, Nginx defaults to listening on port 80. return test_port == self.DEFAULT_LISTEN_PORT else: return test_port == matching_port def _vhost_listening_on_port_no_ssl(self, vhost, port): found_matching_port = False if len(vhost.addrs) == 0: # if there are no listen directives at all, Nginx defaults to # listening on port 80. found_matching_port = (port == self.DEFAULT_LISTEN_PORT) else: for addr in vhost.addrs: if self._port_matches(port, addr.get_port()) and addr.ssl == False: found_matching_port = True if found_matching_port: # make sure we don't have an 'ssl on' directive return not self.parser.has_ssl_on_directive(vhost) else: return False def _get_redirect_ranked_matches(self, target_name, port): """Gets a ranked list of plaintextish port-listening vhosts matching target_name Filter all hosts for those listening on port without using ssl. Rank by how well these match target_name. :param str target_name: The name to match :param str port: port number as a string :returns: list of dicts containing the vhost, the matching name, and the numerical rank :rtype: list """ all_vhosts = self.parser.get_vhosts() def _vhost_matches(vhost, port): return self._vhost_listening_on_port_no_ssl(vhost, port) matching_vhosts = [vhost for vhost in all_vhosts if _vhost_matches(vhost, port)] # We can use this ranking function because sslishness doesn't matter to us, and # there shouldn't be conflicting plaintextish servers listening on 80. return self._rank_matches_by_name_and_ssl(matching_vhosts, target_name) def get_all_names(self): """Returns all names found in the Nginx Configuration. :returns: All ServerNames, ServerAliases, and reverse DNS entries for virtual host addresses :rtype: set """ all_names = set() for vhost in self.parser.get_vhosts(): all_names.update(vhost.names) for addr in vhost.addrs: host = addr.get_addr() if common.hostname_regex.match(host): # If it's a hostname, add it to the names. all_names.add(host) elif not common.private_ips_regex.match(host): # If it isn't a private IP, do a reverse DNS lookup try: if addr.ipv6: host = addr.get_ipv6_exploded() socket.inet_pton(socket.AF_INET6, host) else: socket.inet_pton(socket.AF_INET, host) all_names.add(socket.gethostbyaddr(host)[0]) except (socket.error, socket.herror, socket.timeout): continue return util.get_filtered_names(all_names) def _get_snakeoil_paths(self): # TODO: generate only once tmp_dir = os.path.join(self.config.work_dir, "snakeoil") le_key = crypto_util.init_save_key( key_size=1024, key_dir=tmp_dir, keyname="key.pem") key = OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, le_key.pem) cert = acme_crypto_util.gen_ss_cert(key, domains=[socket.gethostname()]) cert_pem = OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, cert) cert_file, cert_path = util.unique_file( os.path.join(tmp_dir, "cert.pem"), mode="wb") with cert_file: cert_file.write(cert_pem) return cert_path, le_key.file def _make_server_ssl(self, vhost): """Make a server SSL. Make a server SSL by adding new listen and SSL directives. :param vhost: The vhost to add SSL to. :type vhost: :class:`~certbot_nginx.obj.VirtualHost` """ ipv6info = self.ipv6_info(self.config.tls_sni_01_port) ipv6_block = [''] ipv4_block = [''] # If the vhost was implicitly listening on the default Nginx port, # have it continue to do so. if len(vhost.addrs) == 0: listen_block = [['\n ', 'listen', ' ', self.DEFAULT_LISTEN_PORT]] self.parser.add_server_directives(vhost, listen_block) if vhost.ipv6_enabled(): ipv6_block = ['\n ', 'listen', ' ', '[::]:{0}'.format(self.config.tls_sni_01_port), ' ', 'ssl'] if not ipv6info[1]: # ipv6only=on is absent in global config ipv6_block.append(' ') ipv6_block.append('ipv6only=on') if vhost.ipv4_enabled(): ipv4_block = ['\n ', 'listen', ' ', '{0}'.format(self.config.tls_sni_01_port), ' ', 'ssl'] snakeoil_cert, snakeoil_key = self._get_snakeoil_paths() ssl_block = ([ ipv6_block, ipv4_block, ['\n ', 'ssl_certificate', ' ', snakeoil_cert], ['\n ', 'ssl_certificate_key', ' ', snakeoil_key], ['\n ', 'include', ' ', self.mod_ssl_conf], ['\n ', 'ssl_dhparam', ' ', self.ssl_dhparams], ]) self.parser.add_server_directives( vhost, ssl_block) ################################## # enhancement methods (IInstaller) ################################## def supported_enhancements(self): # pylint: disable=no-self-use """Returns currently supported enhancements.""" return ['redirect', 'ensure-http-header', 'staple-ocsp'] def enhance(self, domain, enhancement, options=None): """Enhance configuration. :param str domain: domain to enhance :param str enhancement: enhancement type defined in :const:`~certbot.constants.ENHANCEMENTS` :param options: options for the enhancement See :const:`~certbot.constants.ENHANCEMENTS` documentation for appropriate parameter. """ try: return self._enhance_func[enhancement](domain, options) except (KeyError, ValueError): raise errors.PluginError( "Unsupported enhancement: {0}".format(enhancement)) except errors.PluginError: logger.warning("Failed %s for %s", enhancement, domain) raise def _has_certbot_redirect(self, vhost, domain): test_redirect_block = _test_block_from_block(_redirect_block_for_domain(domain)) return vhost.contains_list(test_redirect_block) def _set_http_header(self, domain, header_substring): """Enables header identified by header_substring on domain. If the vhost is listening plaintextishly, separates out the relevant directives into a new server block, and only add header directive to HTTPS block. :param str domain: the domain to enable header for. :param str header_substring: String to uniquely identify a header. e.g. Strict-Transport-Security, Upgrade-Insecure-Requests :returns: Success :raises .errors.PluginError: If no viable HTTPS host can be created or set with header header_substring. """ vhosts = self.choose_vhosts(domain) if not vhosts: raise errors.PluginError( "Unable to find corresponding HTTPS host for enhancement.") for vhost in vhosts: if vhost.has_header(header_substring): raise errors.PluginEnhancementAlreadyPresent( "Existing %s header" % (header_substring)) # if there is no separate SSL block, break the block into two and # choose the SSL block. if vhost.ssl and any([not addr.ssl for addr in vhost.addrs]): _, vhost = self._split_block(vhost) header_directives = [ ['\n ', 'add_header', ' ', header_substring, ' '] + constants.HEADER_ARGS[header_substring], ['\n']] self.parser.add_server_directives(vhost, header_directives) def _add_redirect_block(self, vhost, domain): """Add redirect directive to vhost """ redirect_block = _redirect_block_for_domain(domain) self.parser.add_server_directives( vhost, redirect_block, insert_at_top=True) def _split_block(self, vhost, only_directives=None): """Splits this "virtual host" (i.e. this nginx server block) into separate HTTP and HTTPS blocks. :param vhost: The server block to break up into two. :param list only_directives: If this exists, only duplicate these directives when splitting the block. :type vhost: :class:`~certbot_nginx.obj.VirtualHost` :returns: tuple (http_vhost, https_vhost) :rtype: tuple of type :class:`~certbot_nginx.obj.VirtualHost` """ http_vhost = self.parser.duplicate_vhost(vhost, only_directives=only_directives) def _ssl_match_func(directive): return 'ssl' in directive def _ssl_config_match_func(directive): return self.mod_ssl_conf in directive def _no_ssl_match_func(directive): return 'ssl' not in directive # remove all ssl addresses and related directives from the new block for directive in self.SSL_DIRECTIVES: self.parser.remove_server_directives(http_vhost, directive) self.parser.remove_server_directives(http_vhost, 'listen', match_func=_ssl_match_func) self.parser.remove_server_directives(http_vhost, 'include', match_func=_ssl_config_match_func) # remove all non-ssl addresses from the existing block self.parser.remove_server_directives(vhost, 'listen', match_func=_no_ssl_match_func) return http_vhost, vhost def _enable_redirect(self, domain, unused_options): """Redirect all equivalent HTTP traffic to ssl_vhost. If the vhost is listening plaintextishly, separate out the relevant directives into a new server block and add a rewrite directive. .. note:: This function saves the configuration :param str domain: domain to enable redirect for :param unused_options: Not currently used :type unused_options: Not Available """ port = self.DEFAULT_LISTEN_PORT # If there are blocks listening plaintextishly on self.DEFAULT_LISTEN_PORT, # choose the most name-matching one. vhosts = self.choose_redirect_vhosts(domain, port) if not vhosts: logger.info("No matching insecure server blocks listening on port %s found.", self.DEFAULT_LISTEN_PORT) return for vhost in vhosts: self._enable_redirect_single(domain, vhost) def _enable_redirect_single(self, domain, vhost): """Redirect all equivalent HTTP traffic to ssl_vhost. If the vhost is listening plaintextishly, separate out the relevant directives into a new server block and add a rewrite directive. .. note:: This function saves the configuration :param str domain: domain to enable redirect for :param `~obj.Vhost` vhost: vhost to enable redirect for """ http_vhost = None if vhost.ssl: http_vhost, _ = self._split_block(vhost, ['listen', 'server_name']) # Add this at the bottom to get the right order of directives return_404_directive = [['\n ', 'return', ' ', '404']] self.parser.add_server_directives(http_vhost, return_404_directive) vhost = http_vhost if self._has_certbot_redirect(vhost, domain): logger.info("Traffic on port %s already redirecting to ssl in %s", self.DEFAULT_LISTEN_PORT, vhost.filep) else: # Redirect plaintextish host to https self._add_redirect_block(vhost, domain) logger.info("Redirecting all traffic on port %s to ssl in %s", self.DEFAULT_LISTEN_PORT, vhost.filep) def _enable_ocsp_stapling(self, domain, chain_path): """Include OCSP response in TLS handshake :param str domain: domain to enable OCSP response for :param chain_path: chain file path :type chain_path: `str` or `None` """ vhosts = self.choose_vhosts(domain) for vhost in vhosts: self._enable_ocsp_stapling_single(vhost, chain_path) def _enable_ocsp_stapling_single(self, vhost, chain_path): """Include OCSP response in TLS handshake :param str vhost: vhost to enable OCSP response for :param chain_path: chain file path :type chain_path: `str` or `None` """ if self.version < (1, 3, 7): raise errors.PluginError("Version 1.3.7 or greater of nginx " "is needed to enable OCSP stapling") if chain_path is None: raise errors.PluginError( "--chain-path is required to enable " "Online Certificate Status Protocol (OCSP) stapling " "on nginx >= 1.3.7.") stapling_directives = [ ['\n ', 'ssl_trusted_certificate', ' ', chain_path], ['\n ', 'ssl_stapling', ' ', 'on'], ['\n ', 'ssl_stapling_verify', ' ', 'on'], ['\n']] try: self.parser.add_server_directives(vhost, stapling_directives) except errors.MisconfigurationError as error: logger.debug(error) raise errors.PluginError("An error occurred while enabling OCSP " "stapling for {0}.".format(vhost.names)) self.save_notes += ("OCSP Stapling was enabled " "on SSL Vhost: {0}.\n".format(vhost.filep)) self.save_notes += "\tssl_trusted_certificate {0}\n".format(chain_path) self.save_notes += "\tssl_stapling on\n" self.save_notes += "\tssl_stapling_verify on\n" ###################################### # Nginx server management (IInstaller) ###################################### def restart(self): """Restarts nginx server. :raises .errors.MisconfigurationError: If either the reload fails. """ nginx_restart(self.conf('ctl'), self.nginx_conf) def config_test(self): # pylint: disable=no-self-use """Check the configuration of Nginx for errors. :raises .errors.MisconfigurationError: If config_test fails """ try: util.run_script([self.conf('ctl'), "-c", self.nginx_conf, "-t"]) except errors.SubprocessError as err: raise errors.MisconfigurationError(str(err)) def _verify_setup(self): """Verify the setup to ensure safe operating environment. Make sure that files/directories are setup with appropriate permissions Aim for defensive coding... make sure all input files have permissions of root. """ uid = os.geteuid() util.make_or_verify_dir( self.config.work_dir, core_constants.CONFIG_DIRS_MODE, uid) util.make_or_verify_dir( self.config.backup_dir, core_constants.CONFIG_DIRS_MODE, uid) util.make_or_verify_dir( self.config.config_dir, core_constants.CONFIG_DIRS_MODE, uid) def get_version(self): """Return version of Nginx Server. Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7)) :returns: version :rtype: tuple :raises .PluginError: Unable to find Nginx version or version is unsupported """ try: proc = subprocess.Popen( [self.conf('ctl'), "-c", self.nginx_conf, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) text = proc.communicate()[1] # nginx prints output to stderr except (OSError, ValueError) as error: logger.debug(error, exc_info=True) raise errors.PluginError( "Unable to run %s -V" % self.conf('ctl')) version_regex = re.compile(r"nginx version: ([^/]+)/([0-9\.]*)", re.IGNORECASE) version_matches = version_regex.findall(text) sni_regex = re.compile(r"TLS SNI support enabled", re.IGNORECASE) sni_matches = sni_regex.findall(text) ssl_regex = re.compile(r" --with-http_ssl_module") ssl_matches = ssl_regex.findall(text) if not version_matches: raise errors.PluginError("Unable to find Nginx version") if not ssl_matches: raise errors.PluginError( "Nginx build is missing SSL module (--with-http_ssl_module).") if not sni_matches: raise errors.PluginError("Nginx build doesn't support SNI") product_name, product_version = version_matches[0] if product_name != 'nginx': logger.warning("NGINX derivative %s is not officially supported by" " certbot", product_name) nginx_version = tuple([int(i) for i in product_version.split(".")]) # nginx < 0.8.48 uses machine hostname as default server_name instead of # the empty string if nginx_version < (0, 8, 48): raise errors.NotSupportedError("Nginx version must be 0.8.48+") return nginx_version def more_info(self): """Human-readable string to help understand the module""" return ( "Configures Nginx to authenticate and install HTTPS.{0}" "Server root: {root}{0}" "Version: {version}".format( os.linesep, root=self.parser.config_root, version=".".join(str(i) for i in self.version)) ) ################################################### # Wrapper functions for Reverter class (IInstaller) ################################################### def save(self, title=None, temporary=False): """Saves all changes to the configuration files. :param str title: The title of the save. If a title is given, the configuration will be saved as a new checkpoint and put in a timestamped directory. :param bool temporary: Indicates whether the changes made will be quickly reversed in the future (ie. challenges) :raises .errors.PluginError: If there was an error in an attempt to save the configuration, or an error creating a checkpoint """ save_files = set(self.parser.parsed.keys()) self.add_to_checkpoint(save_files, self.save_notes, temporary) self.save_notes = "" # Change 'ext' to something else to not override existing conf files self.parser.filedump(ext='') if title and not temporary: self.finalize_checkpoint(title) def recovery_routine(self): """Revert all previously modified files. Reverts all modified files that have not been saved as a checkpoint :raises .errors.PluginError: If unable to recover the configuration """ super(NginxConfigurator, self).recovery_routine() self.new_vhost = None self.parser.load() def revert_challenge_config(self): """Used to cleanup challenge configurations. :raises .errors.PluginError: If unable to revert the challenge config. """ self.revert_temporary_config() self.new_vhost = None self.parser.load() def rollback_checkpoints(self, rollback=1): """Rollback saved checkpoints. :param int rollback: Number of checkpoints to revert :raises .errors.PluginError: If there is a problem with the input or the function is unable to correctly revert the configuration """ super(NginxConfigurator, self).rollback_checkpoints(rollback) self.new_vhost = None self.parser.load() ########################################################################### # Challenges Section for IAuthenticator ########################################################################### def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use """Return list of challenge preferences.""" return [challenges.TLSSNI01, challenges.HTTP01] # Entry point in main.py for performing challenges def perform(self, achalls): """Perform the configuration related challenge. This function currently assumes all challenges will be fulfilled. If this turns out not to be the case in the future. Cleanup and outstanding challenges will have to be designed better. """ self._chall_out += len(achalls) responses = [None] * len(achalls) sni_doer = tls_sni_01.NginxTlsSni01(self) http_doer = http_01.NginxHttp01(self) for i, achall in enumerate(achalls): # Currently also have chall_doer hold associated index of the # challenge. This helps to put all of the responses back together # when they are all complete. if isinstance(achall.chall, challenges.HTTP01): http_doer.add_chall(achall, i) else: # tls-sni-01 sni_doer.add_chall(achall, i) sni_response = sni_doer.perform() http_response = http_doer.perform() # Must restart in order to activate the challenges. # Handled here because we may be able to load up other challenge types self.restart() # Go through all of the challenges and assign them to the proper place # in the responses return value. All responses must be in the same order # as the original challenges. for chall_response, chall_doer in ((sni_response, sni_doer), (http_response, http_doer)): for i, resp in enumerate(chall_response): responses[chall_doer.indices[i]] = resp return responses # called after challenges are performed def cleanup(self, achalls): """Revert all challenges.""" self._chall_out -= len(achalls) # If all of the challenges have been finished, clean up everything if self._chall_out <= 0: self.revert_challenge_config() self.restart() def _test_block_from_block(block): test_block = nginxparser.UnspacedList(block) parser.comment_directive(test_block, 0) return test_block[:-1] def _redirect_block_for_domain(domain): updated_domain = domain match_symbol = '=' if util.is_wildcard_domain(domain): match_symbol = '~' updated_domain = updated_domain.replace('.', r'\.') updated_domain = updated_domain.replace('*', '[^.]+') updated_domain = '^' + updated_domain + '$' redirect_block = [[ ['\n ', 'if', ' ', '($host', ' ', match_symbol, ' ', '%s)' % updated_domain, ' '], [['\n ', 'return', ' ', '301', ' ', 'https://$host$request_uri'], '\n ']], ['\n']] return redirect_block def nginx_restart(nginx_ctl, nginx_conf): """Restarts the Nginx Server. .. todo:: Nginx restart is fatal if the configuration references non-existent SSL cert/key files. Remove references to /etc/letsencrypt before restart. :param str nginx_ctl: Path to the Nginx binary. """ try: proc = subprocess.Popen([nginx_ctl, "-c", nginx_conf, "-s", "reload"]) proc.communicate() if proc.returncode != 0: # Maybe Nginx isn't running # Write to temporary files instead of piping because of communication issues on Arch # https://github.com/certbot/certbot/issues/4324 with tempfile.TemporaryFile() as out: with tempfile.TemporaryFile() as err: nginx_proc = subprocess.Popen([nginx_ctl, "-c", nginx_conf], stdout=out, stderr=err) nginx_proc.communicate() if nginx_proc.returncode != 0: # Enter recovery routine... raise errors.MisconfigurationError( "nginx restart failed:\n%s\n%s" % (out.read(), err.read())) except (OSError, ValueError): raise errors.MisconfigurationError("nginx restart failed") # Nginx can take a moment to recognize a newly added TLS SNI servername, so sleep # for a second. TODO: Check for expected servername and loop until it # appears or return an error if looping too long. time.sleep(1) def install_ssl_options_conf(options_ssl, options_ssl_digest): """Copy Certbot's SSL options file into the system's config dir if required.""" return common.install_version_controlled_file(options_ssl, options_ssl_digest, constants.MOD_SSL_CONF_SRC, constants.ALL_SSL_OPTIONS_HASHES) certbot-nginx-0.23.0/certbot_nginx/display_ops.py0000644000076600000240000000257613261244762022056 0ustar bmwstaff00000000000000"""Contains UI methods for Nginx operations.""" import logging import zope.component from certbot import interfaces import certbot.display.util as display_util logger = logging.getLogger(__name__) def select_vhost_multiple(vhosts): """Select multiple Vhosts to install the certificate for :param vhosts: Available Nginx VirtualHosts :type vhosts: :class:`list` of type `~obj.Vhost` :returns: List of VirtualHosts :rtype: :class:`list`of type `~obj.Vhost` """ if not vhosts: return list() tags_list = [vhost.display_repr()+"\n" for vhost in vhosts] # Remove the extra newline from the last entry if len(tags_list): tags_list[-1] = tags_list[-1][:-1] code, names = zope.component.getUtility(interfaces.IDisplay).checklist( "Which server blocks would you like to modify?", tags=tags_list, force_interactive=True) if code == display_util.OK: return_vhosts = _reversemap_vhosts(names, vhosts) return return_vhosts return [] def _reversemap_vhosts(names, vhosts): """Helper function for select_vhost_multiple for mapping string representations back to actual vhost objects""" return_vhosts = list() for selection in names: for vhost in vhosts: if vhost.display_repr().strip() == selection.strip(): return_vhosts.append(vhost) return return_vhosts