├── .codeclimate.yml ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github ├── CONTRIBUTING.md └── workflows │ └── normal.yml ├── .gitignore ├── .mdlrc ├── .mdlrc.style.rb ├── .stylelintignore ├── .stylelintrc.json ├── LICENSE.md ├── README.md ├── bubbly_copy-configs.sh ├── bubbly_generate-statics.sh ├── bubbly_renew-ssl.sh ├── contribute.json ├── nginx-config ├── conf.d │ ├── amplify_log-format.conf │ ├── amplify_stub-status.conf │ ├── bubbly_limits.conf │ ├── expires-map.conf │ ├── nixstats_stub-status.conf │ └── php_sockets.conf ├── directive │ ├── bubbly_gzip.conf │ ├── bubbly_limits_20.conf │ ├── bubbly_limits_server_2k.conf │ ├── bubbly_logs.conf │ ├── bubbly_logs_amplify.conf │ ├── bubbly_logs_off.conf │ ├── bubbly_rock-hard-ssl.conf │ ├── bubbly_security-headers.conf │ ├── bubbly_security-headers_csp.conf │ ├── bubbly_uploads.conf │ ├── h5bp_cache-file-descriptors.conf │ ├── h5bp_no-transform.conf │ └── h5bp_x-ua-compatible.conf ├── groups │ ├── performance-common.conf │ └── security-common.conf ├── location │ ├── bubbly_errors.conf │ ├── bubbly_extensionless-php.conf │ ├── bubbly_methods.conf │ ├── bubbly_well-known-passthrough.conf │ ├── h5bp_expires.conf │ └── h5bp_protect-system-files.conf ├── mime.types └── sites-available │ ├── bubbly_live.conf │ └── bubbly_verify.conf ├── screenshot_securityheaders.io.png └── screenshot_ssllabs.com.png /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | checks: 3 | argument-count: 4 | config: 5 | threshold: 10 6 | method-complexity: 7 | config: 8 | threshold: 20 9 | method-lines: 10 | config: 11 | threshold: 50 12 | 13 | plugins: 14 | csslint: 15 | enabled: false 16 | # Stylelint is used instead. 17 | duplication: 18 | enabled: true 19 | config: 20 | languages: 21 | - javascript 22 | - php 23 | - python 24 | eslint: 25 | enabled: true 26 | channel: "eslint-3" 27 | # config: .eslintrc.json 28 | # ignore: .eslintignore 29 | fixme: 30 | enabled: true 31 | markdownlint: 32 | enabled: true 33 | # initial: .mdlrc 34 | # config: .mdlrc.style.rb 35 | phpcodesniffer: 36 | enabled: true 37 | checks: 38 | Generic Files LineLength TooLong: 39 | enabled: false 40 | Generic WhiteSpace DisallowTabIndent TabsUsed: 41 | enabled: false 42 | PSR2 ControlStructures ControlStructureSpacing SpacingAfterOpenBrace: 43 | enabled: false 44 | PSR2 ControlStructures ControlStructureSpacing SpaceBeforeCloseBrace: 45 | enabled: false 46 | Squiz ControlStructures ForEachLoopDeclaration SpaceAfterOpen: 47 | enabled: false 48 | Squiz ControlStructures ForEachLoopDeclaration SpaceBeforeClose: 49 | enabled: false 50 | Squiz ControlStructures ForLoopDeclaration SpacingAfterOpen: 51 | enabled: false 52 | Squiz ControlStructures ForLoopDeclaration SpacingBeforeClose: 53 | enabled: false 54 | Squiz Functions MultiLineFunctionDeclaration BraceOnSameLine: 55 | enabled: false 56 | phpmd: 57 | enabled: true 58 | checks: 59 | CleanCode/BooleanArgumentFlag: 60 | enabled: false 61 | Controversial/CamelCaseParameterName: 62 | enabled: false 63 | Controversial/CamelCaseVariableName: 64 | enabled: false 65 | shellcheck: 66 | enabled: true 67 | stylelint: 68 | enabled: true 69 | 70 | exclude_patterns: 71 | - "_libs/**" 72 | - "**.min.css" 73 | - "**.custom.css" 74 | - "**.min.js" 75 | - "**.pack.js" 76 | - "**.custom.js" 77 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | *.min.js 2 | *.pack.js 3 | *.custom.js 4 | _libs/* 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true 4 | }, 5 | "extends": "eslint:recommended", 6 | "rules": { 7 | "semi": ["error", "never"], 8 | "indent": ["error", "tab", { "SwitchCase": 1 } ], 9 | "comma-dangle": ["error", "always-multiline"], 10 | "no-cond-assign": ["error", "always"], 11 | "no-console": "off", 12 | "no-mixed-spaces-and-tabs": ["error", "smart-tabs"] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | nginx-config/**/*.conf linguist-language=Nginx 2 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We love pull requests from everyone. Check out our [open issues](https://github.com/eustasy/bubbly/issues), partically anything tagged as Bytesize, for things you can get to work on. By participating in this project, you agree to abide by the project [code of conduct](https://github.com/eustasy/bubbly/blob/master/.github/CODE_OF_CONDUCT.md). 4 | 5 | ## Making Changes 6 | 7 | We recommend forking the repository, and then cloning your new repo. 8 | 9 | git clone git@github.com:your-username/bubbly.git 10 | 11 | Once you've made changes and committed them in your fork, preferably on a nicely named branch with descriptive commit messages, you can move on to [Creating a Pull Request](#creating-a-pull-request). 12 | 13 | ### Filing an Issue 14 | 15 | Filing a new issue is a partially self-documenting process, as the [`.github/ISSUE_TEMPLATE.md`](https://github.com/eustasy/bubbly/blob/master/.github/ISSUE_TEMPLATE.md) file is automatically loaded to be filled out by the user. 16 | 17 | [`File an Issue`](https://github.com/eustasy/bubbly/issues/new) 18 | 19 | ### Creating a Pull Request 20 | 21 | Similar to Filing an Issue, Creating a Pull Request is partially self-documenting as the [`.github/PULL_REQUEST_TEMPLATE.md`](https://github.com/eustasy/bubbly/blob/master/.github/PULL_REQUEST_TEMPLATE.md) file is automatically loaded into the system. First though, you will need to have [made the changes](#making-changes) in your fork. 22 | 23 | [`Create a Pull Request`](https://github.com/eustasy/bubbly/compare/) 24 | 25 | ## Current State 26 | 27 | ### Cipher Sources 28 | 29 | In [`nginx-config/directive/bubbly_rock-hard-ssl.conf`](https://github.com/eustasy/bubbly/blob/master/nginx-config/directive/bubbly_rock-hard-ssl.conf) you will find a list of three cipher suite options at the bottom. It is imperative that these are kept as up to date as possible. All were up to date as of 2017-08-17. 30 | 31 | #### [Cipher List](https://cipherli.st) 32 | 33 | Super-modern, probably not suitable for production, very secure. 34 | 35 | - Grade A (A+ with HSTS at >= 6 Months) 36 | - 100 % Security 37 | - Low Compatibility 38 | - - No Android 2 39 | - - No Java 40 | - - No IE < 11 41 | - Robust Forward Secrecy 42 | 43 | ``` 44 | ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'; 45 | ``` 46 | 47 | #### [DEFAULT] [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/) using the setting "Nginx for Modern Browsers". 48 | 49 | Modern, no XP, secure. 50 | 51 | - Grade A (A+ with HSTS at >= 6 Months) 52 | - 90 % Security 53 | - Medium Compatibility 54 | - - No Java 6 (No DH parameters > 1024 bits) 55 | - - No IE on XP 56 | - Robust Forward Secrecy 57 | 58 | ``` 59 | ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; 60 | ``` 61 | 62 | #### [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/) using the setting "Nginx for Intermediate Browsers" 63 | 64 | Intermediate, no IE <= 6, less secure. 65 | 66 | - Grade A- 67 | - 90 % Security 68 | - High Compatibility 69 | - - No Java 6 (No DH parameters > 1024 bits) 70 | - - No IE 6 71 | - Some Forward Secrecy 72 | 73 | ``` 74 | 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'; 75 | ``` 76 | 77 | ### Headers 78 | 79 | Various headers are delivered from various configuration files. This list should help source any undesired headers you see being sent. Some headers can be sent from multiple locations. 80 | 81 | - [`nginx-config/directive/bubbly_security-headers.conf`](https://github.com/eustasy/bubbly/blob/master/nginx-config/directive/bubbly_security-headers.conf) 82 | - - `Access-Control-Allow-Origin` 83 | - - `Content-Security-Policy-Report-Only` and `X-Content-Security-Policy-Report-Only` and `X-WebKit-CSP-Report-Only` 84 | - - `Content-Security-Policy` and `X-Content-Security-Policy` and `X-WebKit-CSP` 85 | - - `Content-Type-Options` and `X-Content-Type-Options` 86 | - - `Expect-CT` 87 | - - `Frame-Options` and `X-Frame-Options` 88 | - - `Referrer-Policy` 89 | - - `Server` 90 | - - `Strict-Transport-Security` 91 | - - `XSS-Protection` and `X-XSS-Protection` 92 | - [`nginx-config/location/h5bp_expires.conf`](https://github.com/eustasy/bubbly/blob/master/nginx-config/location/h5bp_expires.conf) 93 | - - `Cache-Control` 94 | - [`nginx-config/directive/h5bp_x-ua-compatible.conf`](https://github.com/eustasy/bubbly/blob/master/nginx-config/directive/h5bp_x-ua-compatible.conf) 95 | - - `UA-Compatible` and `X-UA-Compatible` 96 | - [`nginx-config/directive/h5bp_no-transform.conf`](https://github.com/eustasy/bubbly/blob/master/nginx-config/directive/h5bp_no-transform.conf) 97 | - - `Cache-Control` 98 | - [`nginx-config/location/bubbly_extensionless-php.conf`](https://github.com/eustasy/bubbly/blob/master/nginx-config/location/bubbly_extensionless-php.conf) 99 | - - Suppresses `Powered-By` and `X-Powered-By` 100 | - [`nginx-config/directive/bubbly_rock-hard-ssl.conf`](https://github.com/eustasy/bubbly/blob/master/nginx-config/directive/bubbly_rock-hard-ssl.conf) 101 | 102 | ## Contact Points 103 | 104 | For any security concerns arising from the state of this repository, please contact [security@eustasy.org](mailto:security@eustasy.org) 105 | -------------------------------------------------------------------------------- /.github/workflows/normal.yml: -------------------------------------------------------------------------------- 1 | name: Normal 2 | 3 | on: 4 | schedule: 5 | # * is a special character in YAML so you have to quote this string 6 | - cron: '30 5,17 * * *' 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | normal-tests: 14 | strategy: 15 | matrix: 16 | php-version: [ "8.2", "8.1", "8.0", "7.4" ] 17 | runs-on: ubuntu-latest 18 | steps: 19 | 20 | - name: Install PHP ${{ matrix.php-version }} 21 | uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: ${{ matrix.php-version }} 24 | #extensions: mbstring 25 | 26 | - name: Install Node.js 27 | uses: actions/setup-node@v3 28 | with: 29 | # Version Spec of the version to use in SemVer notation. 30 | # It also emits such aliases as lts, latest, nightly and canary builds 31 | # Examples: 12.x, 10.15.1, >=10.15.0, lts/Hydrogen, 16-nightly, latest, node 32 | node-version: 'lts/*' 33 | # Set this option if you want the action to check for the latest available version 34 | # that satisfies the version spec. 35 | # It will only get affect for lts Nodejs versions (12.x, >=10.15.0, lts/Hydrogen). 36 | # Default: false 37 | check-latest: true 38 | 39 | - name: Checkout Repo 40 | uses: actions/checkout@v3 41 | 42 | - name: Copy .env 43 | run: php -r "file_exists('.env') || copy('.env.example', '.env');" 44 | 45 | - name: .Normal - Setup 46 | run: git clone https://github.com/eustasy/.normal.git && cd .normal && git checkout af240a6c8960177bcb1d07815732df7eb15970c1 && cd ../ && npm install --silent -g npm && npm install --silent -g acorn 47 | 48 | - name: .Normal - Check JavaScript 49 | run: ./.normal/check-javascript.sh 50 | 51 | - name: .Normal - Check PHP 52 | run: ./.normal/check-php.sh 53 | 54 | - name: .Normal - Check SQL 55 | uses: Bidaya0/sql-lint-in-action@v0.0.2 56 | 57 | - name: .Normal - Check JSON 58 | run: php .normal/check-json.php 59 | 60 | - name: .Normal - Check XML 61 | run: php .normal/check-xml.php 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | rules "MD001", # header levels increment by 1 2 | "MD002", # first header should be level 1 3 | "MD003", # header style - atx 4 | "MD004", # unordered list style - asterick 5 | "MD005", # don't allow inconsistent indentation for list items 6 | "MD006", # start bullets at beginning of line 7 | "MD007", # unordered list indentation should be 4 spaces 8 | "MD009", # trailing spaces not allowed 9 | "MD010", # hard tabs not allowed 10 | "MD011", # alert on reversed link syntax 11 | "MD012", # alert on multiple consecutive blank lines 12 | "MD013", # line length should be no more than 80 characters 13 | "MD014", # dollars signs not before commands w/o showing output 14 | "MD018", # must have space after header style 15 | "MD019", # no multiple spaces after header style 16 | "MD022", # headers should be surrounded by blank lines 17 | "MD023", # headers must start at the beginning of the line 18 | "MD024", # no multiple headers with same content 19 | "MD025", # only 1 level 1 header 20 | "MD026", # no trailing punctuation in headers 21 | "MD027", # no multiple spaces after blockquote symbol 22 | "MD028", # no blank lines within blockquote 23 | "MD029", # ordered list item prefix, shoulded be ordered 24 | "MD030", # spaces after list markers 25 | "MD031", # fenced code blocks need line surrounding 26 | "MD032", # lists should be surrounded by line 27 | "MD033", # Inline HTML 28 | "MD034", # Bare URL used 29 | "MD035", # Horizontal rule style 30 | "MD036", # Emphasis used instead of a header 31 | "MD037", # Spaces inside emphasis markers 32 | "MD038", # Spaces inside code span elements 33 | "MD039", # Spaces inside link text 34 | "MD040", # Fenced code blocks should have a language specified 35 | "MD041" # First line in file should be a top level header 36 | style ".mdlrc.style.rb" 37 | -------------------------------------------------------------------------------- /.mdlrc.style.rb: -------------------------------------------------------------------------------- 1 | all 2 | rule 'header-style', :style => "atx" 3 | rule 'line_length', :line_length => 180 4 | rule 'ol-prefix', :style => "ordered" 5 | rule 'ul-style', :style => "asterisk" 6 | 7 | exclude_rule 'first-header-h1' 8 | exclude_rule 'first-line-h1' 9 | exclude_rule 'no-inline-html' 10 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | *.min.css 2 | *.custom.css 3 | _libs/* 4 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "rules": { 4 | 5 | "color-hex-case": "lower", 6 | "color-hex-length": "long", 7 | "color-named": "never", 8 | "color-no-invalid-hex": true, 9 | 10 | "font-family-name-quotes": "always-unless-keyword", 11 | "font-family-no-duplicate-names": true, 12 | 13 | "font-weight-notation": "numeric", 14 | 15 | "function-calc-no-unspaced-operator": true, 16 | "function-comma-newline-after": "always-multi-line", 17 | "function-comma-newline-before": "never-multi-line", 18 | "function-comma-space-after": "always", 19 | "function-comma-space-before": "never", 20 | "function-linear-gradient-no-nonstandard-direction": true, 21 | "function-max-empty-lines": 0, 22 | "function-name-case": "lower", 23 | "function-parentheses-newline-inside": "always-multi-line", 24 | "function-parentheses-space-inside": "never-single-line", 25 | "function-url-no-scheme-relative": true, 26 | "function-url-quotes": "always", 27 | "function-url-scheme-whitelist": ["https", "data"], 28 | "function-whitespace-after": "always", 29 | 30 | "number-leading-zero": "always", 31 | "number-max-precision": 2, 32 | "number-no-trailing-zeros": true, 33 | 34 | "string-no-newline": true, 35 | "string-quotes": "single", 36 | 37 | "length-zero-no-unit": true, 38 | 39 | "time-min-milliseconds": 100, 40 | 41 | "unit-case": "lower", 42 | "unit-no-unknown": true, 43 | 44 | "value-keyword-case": "lower", 45 | "value-no-vendor-prefix": true, 46 | 47 | "value-list-comma-newline-after": "always-multi-line", 48 | "value-list-comma-newline-before": "never-multi-line", 49 | "value-list-comma-space-after": "always-single-line", 50 | "value-list-comma-space-before": "never", 51 | "value-list-max-empty-lines": 0, 52 | 53 | "custom-property-empty-line-before": "never", 54 | 55 | "shorthand-property-no-redundant-values": true, 56 | 57 | "property-case": "lower", 58 | "property-no-unknown": true, 59 | "property-no-vendor-prefix": true, 60 | 61 | "keyframe-declaration-no-important": true, 62 | 63 | "declaration-bang-space-after": "never", 64 | "declaration-bang-space-before": "always", 65 | "declaration-colon-newline-after": "always-multi-line", 66 | "declaration-colon-space-after": "always-single-line", 67 | "declaration-colon-space-before": "never", 68 | "declaration-empty-line-before": "never", 69 | "declaration-no-important": true, 70 | 71 | "declaration-block-no-duplicate-properties": [ true, { 72 | "ignore": ["consecutive-duplicates-with-different-values"] 73 | }], 74 | "declaration-block-no-redundant-longhand-properties": true, 75 | "declaration-block-no-shorthand-property-overrides": true, 76 | "declaration-block-semicolon-newline-after": "always-multi-line", 77 | "declaration-block-semicolon-newline-before": "never-multi-line", 78 | "declaration-block-semicolon-space-after": "always-single-line", 79 | "declaration-block-semicolon-space-before": "never", 80 | "declaration-block-single-line-max-declarations": 1, 81 | "declaration-block-trailing-semicolon": "always", 82 | 83 | "block-closing-brace-empty-line-before": "never", 84 | "block-closing-brace-newline-after": "always", 85 | "block-closing-brace-newline-before": "always-multi-line", 86 | "block-closing-brace-space-before": "always-single-line", 87 | "block-no-empty": true, 88 | "block-opening-brace-newline-after": "always-multi-line", 89 | "block-opening-brace-space-after": "always-single-line", 90 | "block-opening-brace-space-before": "always", 91 | 92 | "selector-attribute-brackets-space-inside": "never", 93 | "selector-attribute-operator-space-after": "never", 94 | "selector-attribute-operator-space-before": "never", 95 | "selector-attribute-quotes": "always", 96 | "selector-combinator-space-after": "always", 97 | "selector-combinator-space-before": "always", 98 | "selector-descendant-combinator-no-non-space": true, 99 | "selector-no-vendor-prefix": true, 100 | "selector-pseudo-class-case": "lower", 101 | "selector-pseudo-class-no-unknown": true, 102 | "selector-pseudo-class-parentheses-space-inside": "always", 103 | "selector-pseudo-element-case": "lower", 104 | "selector-pseudo-element-colon-notation": "double", 105 | "selector-pseudo-element-no-unknown": true, 106 | "selector-type-case": "lower", 107 | "selector-type-no-unknown": true, 108 | "selector-max-empty-lines": 0, 109 | "selector-max-id": 0, 110 | "selector-max-universal": 0, 111 | 112 | "selector-list-comma-newline-after": "always", 113 | "selector-list-comma-newline-before": "never-multi-line", 114 | "selector-list-comma-space-after": "always-single-line", 115 | "selector-list-comma-space-before": "never", 116 | 117 | "rule-empty-line-before": "never", 118 | 119 | "media-feature-colon-space-after": "always", 120 | "media-feature-colon-space-before": "never", 121 | "media-feature-name-case": "lower", 122 | "media-feature-name-no-unknown": true, 123 | "media-feature-name-no-vendor-prefix": true, 124 | "media-feature-parentheses-space-inside": "always", 125 | "media-feature-range-operator-space-after": "always", 126 | "media-feature-range-operator-space-before": "always", 127 | 128 | "media-query-list-comma-newline-after": "always", 129 | "media-query-list-comma-newline-before": "never-multi-line", 130 | "media-query-list-comma-space-after": "always-single-line", 131 | "media-query-list-comma-space-before": "never", 132 | 133 | "at-rule-empty-line-before": "never", 134 | "at-rule-name-case": "lower", 135 | "at-rule-name-newline-after": "always-multi-line", 136 | "at-rule-name-space-after": "always-single-line", 137 | "at-rule-no-unknown": true, 138 | "at-rule-no-vendor-prefix": true, 139 | "at-rule-semicolon-newline-after": "always", 140 | 141 | "comment-empty-line-before": "never", 142 | "comment-no-empty": true, 143 | "comment-whitespace-inside": "always", 144 | 145 | "indentation": [ "tab", { 146 | "ignore": ["inside-parens", "param", "value"] 147 | }], 148 | "max-empty-lines": 1, 149 | "no-duplicate-selectors": true, 150 | "no-empty-source": true, 151 | "no-eol-whitespace": true, 152 | "no-extra-semicolons": true, 153 | "no-invalid-double-slash-comments": true, 154 | "no-unknown-animations": true 155 | 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright © 2024 [eustasy](https://eustasy.org) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bubbly 2 | 3 | ##### For configuring Certbot with Nginx as quickly and securely as possible. 4 | 5 | [![Normal](https://github.com/eustasy/Bubbly/actions/workflows/normal.yml/badge.svg)](https://github.com/eustasy/Bubbly/actions/workflows/normal.yml) 6 | [![Code Climate](https://codeclimate.com/github/eustasy/bubbly/badges/gpa.svg)](https://codeclimate.com/github/eustasy/bubbly) 7 | 8 | If you want an instant A+ score on Qualys [SSL Labs](https://www.ssllabs.com/ssltest/analyze.html?d=lewisgoddard.me.uk) and A score on [SecurityHeaders.io](https://securityheaders.io/?q=lewisgoddard.me.uk&followRedirects=on), then this is what you'll need to do. You won't need any familiarity with [Certbot](https://github.com/certbot/certbot), [Let's Encrypt](https://letsencrypt.org/), the ACME spec, or SSL in general, just basic Nginx configuration. 9 | 10 | **1. Install Certbot and Clone Bubbly** 11 | 12 | We'll start off by cloning the project into the home folder with git. 13 | 14 | ``` 15 | cd && 16 | sudo apt install git certbot && 17 | git clone https://github.com/eustasy/bubbly 18 | ``` 19 | 20 | **2. Generate Statics** 21 | 22 | Generate the static keys once per server. 23 | 24 | ``` 25 | ~/bubbly/bubbly_generate-statics.sh 26 | ``` 27 | 28 | As it will warn, this will take a while. 29 | 30 | Have a seat. 31 | 32 | **3. Copy config blocks** 33 | 34 | When you've gone and made something in the 15 minutes that could well take, or you've just set up a new SSH session, copy the Nginx configuration over to the Nginx area. 35 | 36 | ``` 37 | ~/bubbly/bubbly_copy-configs.sh 38 | ``` 39 | 40 | **4. Configure & Enable Verification** 41 | 42 | Copy the verification site template and replace the instances of `example.com` in the file with your actual domain name. 43 | 44 | ``` 45 | sudo cp /etc/nginx/sites-available/bubbly_verify.conf /etc/nginx/sites-available/example.com.conf 46 | sudo nano /etc/nginx/sites-available/example.com.conf 47 | ``` 48 | 49 | Use `Ctrl` and `\` to initiate a search and replace for `example.com` with your domain. 50 | 51 | ``` 52 | sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf 53 | sudo nginx -t && sudo service nginx reload 54 | ``` 55 | 56 | Alternatively, you can simply add `include location/bubbly_well-known-passthrough.conf;` to an existing site you want to continue working while we upgrade. 57 | 58 | 59 | **5. Fetch Certificates** 60 | 61 | Fetch your certificates like this: 62 | 63 | ``` 64 | ~/bubbly/bubbly_renew-ssl.sh -d example.com -d www.example.com 65 | ``` 66 | 67 | It will ask for the root password, and an email address, so hang around, it shouldn't take more than a few seconds. 68 | 69 | It should also tell you Certbot set up auto-renewals in the background. 70 | 71 | **6. Start using the Certificates** 72 | 73 | Remove the verification config you just made, and replace it with a live version of the site. You'll need to more carefully review the `[OPTION]`s in this file, as you'll also need to change the certificate location to match the domain name you requested. Consider taking a look at the `[OPTION]`s and `[WARNING]`s in other linked config files. 74 | 75 | ``` 76 | sudo rm /etc/nginx/sites-available/example.com.conf 77 | sudo cp /etc/nginx/sites-available/bubbly_live.conf /etc/nginx/sites-available/example.com.conf 78 | sudo nano /etc/nginx/sites-available/example.com.conf 79 | ``` 80 | 81 | Use `Ctrl` and `\` to initiate a search and replace for `example.com` with your domain. 82 | 83 | ``` 84 | sudo nginx -t && sudo service nginx reload 85 | ``` 86 | 87 | --- 88 | 89 | ![Screenshot of SSLLabs.com](https://raw.githubusercontent.com/eustasy/bubbly/master/screenshot_ssllabs.com.png) 90 | 91 | ![Screenshot of SecurityHeaders.io](https://raw.githubusercontent.com/eustasy/bubbly/master/screenshot_securityheaders.io.png) 92 | -------------------------------------------------------------------------------- /bubbly_copy-configs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo rsync -avh bubbly/nginx-config/ /etc/nginx/ 4 | -------------------------------------------------------------------------------- /bubbly_generate-statics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | printf "WARNING: The generation of static keys and parameters may take several minutes, and will consume a considerable amount of processing power. If not favourable on a production machine, new keys can be generated elsewhere and copied over a secure connection. " 3 | read -rsp $'Press any key to continue, or Ctrl+C to cancel...\n' -n1 key 4 | sudo mkdir -p /etc/nginx/ssl && 5 | sudo openssl rand -out /etc/nginx/ssl/ticket.key 80 && 6 | sudo openssl dhparam -out /etc/nginx/ssl/dhparam3.pem 3072 7 | -------------------------------------------------------------------------------- /bubbly_renew-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo mkdir -p /tmp/bubbly-authenticator 4 | 5 | sudo certbot certonly \ 6 | --rsa-key-size 3072 \ 7 | --server https://acme-v02.api.letsencrypt.org/directory \ 8 | --authenticator webroot \ 9 | --webroot-path=/tmp/bubbly-authenticator \ 10 | --agree-tos \ 11 | --force-renew \ 12 | "$@" 13 | 14 | sudo service nginx reload 15 | -------------------------------------------------------------------------------- /contribute.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bubbly: Certbot for Nginx", 3 | "description": "", 4 | "repository": { 5 | "url": "https://github.com/eustasy/bubbly", 6 | "license": "MIT", 7 | "type": "git", 8 | "tests": "https://github.com/eustasy/Bubbly/actions/workflows/normal.yml", 9 | "clone": "https://github.com/eustasy/bubbly.git" 10 | }, 11 | "bugs": { 12 | "list": "https://github.com/eustasy/bubbly/issues", 13 | "report": "https://github.com/eustasy/bubbly/issues/new" 14 | }, 15 | "keywords": [ 16 | "eustasy", 17 | "bubbly", 18 | "certbot", 19 | "nginx" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /nginx-config/conf.d/amplify_log-format.conf: -------------------------------------------------------------------------------- 1 | log_format main_ext '$remote_addr - $remote_user [$time_local] "$request" ' 2 | '$status $body_bytes_sent "$http_referer" ' 3 | '"$http_user_agent" "$http_x_forwarded_for" ' 4 | '"$host" sn="$server_name" ' 5 | 'rt=$request_time ' 6 | 'ua="$upstream_addr" us="$upstream_status" ' 7 | 'ut="$upstream_response_time" ul="$upstream_response_length" ' 8 | 'cs=$upstream_cache_status'; 9 | -------------------------------------------------------------------------------- /nginx-config/conf.d/amplify_stub-status.conf: -------------------------------------------------------------------------------- 1 | # This file creates a stub_status page for Nginx reachable (internal only) at http://127.0.0.1/nginx_status 2 | 3 | # This file is compatible with Nginx Amplify 4 | # https://amplify.nginx.com/docs/guide-how-nginx-amplify-agent-works.html#metrics-from-stub_status 5 | 6 | server { 7 | listen 127.0.0.1:80; 8 | server_name 127.0.0.1; 9 | location /nginx_status { 10 | stub_status on; 11 | allow 127.0.0.1; 12 | deny all; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /nginx-config/conf.d/bubbly_limits.conf: -------------------------------------------------------------------------------- 1 | limit_req_zone $binary_remote_addr zone=reqPerSec1:1m rate=1r/s; 2 | limit_req_zone $binary_remote_addr zone=reqPerSec10:1m rate=10r/s; 3 | limit_req_zone $binary_remote_addr zone=reqPerSec20:1m rate=20r/s; 4 | 5 | limit_conn_zone $binary_remote_addr zone=conPerIP:10m; 6 | limit_conn_zone $server_name zone=conPerServer:10m; 7 | -------------------------------------------------------------------------------- /nginx-config/conf.d/expires-map.conf: -------------------------------------------------------------------------------- 1 | # src=https://raw.githubusercontent.com/h5bp/server-configs-nginx/master/h5bp/web_performance/cache_expiration.conf 2 | 3 | map $sent_http_content_type $expires { 4 | default 1M; 5 | 6 | # No content 7 | "" off; 8 | 9 | # CSS 10 | ~*text/css 1y; 11 | 12 | # Data interchange 13 | ~*application/atom\+xml 1h; 14 | ~*application/rdf\+xml 1h; 15 | ~*application/rss\+xml 1h; 16 | 17 | ~*application/json 0; 18 | ~*application/ld\+json 0; 19 | ~*application/schema\+json 0; 20 | ~*application/geo\+json 0; 21 | ~*application/xml 0; 22 | ~*text/calendar 0; 23 | ~*text/xml 0; 24 | 25 | # Favicon (cannot be renamed!) and cursor images 26 | ~*image/vnd.microsoft.icon 1w; 27 | ~*image/x-icon 1w; 28 | 29 | # HTML 30 | ~*text/html 0; 31 | 32 | # JavaScript 33 | ~*application/javascript 1y; 34 | ~*application/x-javascript 1y; 35 | ~*text/javascript 1y; 36 | 37 | # Manifest files 38 | ~*application/manifest\+json 1w; 39 | ~*application/x-web-app-manifest\+json 0; 40 | ~*text/cache-manifest 0; 41 | 42 | # Markdown 43 | ~*text/markdown 0; 44 | 45 | # Media files 46 | ~*audio/ 1M; 47 | ~*image/ 1M; 48 | ~*video/ 1M; 49 | 50 | # WebAssembly 51 | ~*application/wasm 1y; 52 | 53 | # Web fonts 54 | ~*font/ 1M; 55 | ~*application/vnd.ms-fontobject 1M; 56 | ~*application/x-font-ttf 1M; 57 | ~*application/x-font-woff 1M; 58 | ~*application/font-woff 1M; 59 | ~*application/font-woff2 1M; 60 | 61 | # Other 62 | ~*text/x-cross-domain-policy 1w; 63 | } 64 | -------------------------------------------------------------------------------- /nginx-config/conf.d/nixstats_stub-status.conf: -------------------------------------------------------------------------------- 1 | # This file creates a stub_status page for Nginx reachable (internal only) at http://127.0.0.1:8080/status_page 2 | 3 | # This file is compatible with NixStats (URL change in config required) 4 | # https://help.nixstats.com/en/article/monitoring-nginx-50nu7f/ 5 | 6 | server { 7 | listen 127.0.0.1:8080; 8 | server_name localhost; 9 | location /status_page { 10 | stub_status on; 11 | allow 127.0.0.1; 12 | deny all; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /nginx-config/conf.d/php_sockets.conf: -------------------------------------------------------------------------------- 1 | # List of PHP sockets 2 | upstream php_sockets { 3 | # [OPTION] Specify PHP version for Socket. 4 | # Support from https://www.php.net/supported-versions.php 5 | 6 | # Option 1. [DEFAULT] PHP 8.3 7 | # Supported until 2026-11-23 8 | server unix:/var/run/php/php8.3-fpm.sock; 9 | 10 | # Option 2. [DISABLED] PHP 8.2 11 | # Supported until 2025-12-08 12 | #server unix:/var/run/php/php8.2-fpm.sock; 13 | 14 | # Option 3. [DISABLED] PHP 8.1 15 | # Supported until 2024-11-25 16 | #server unix:/var/run/php/php8.1-fpm.sock; 17 | 18 | # [OPTION] Specify backup socket for PHP. 19 | #server unix:/var/run/php/php8.3-fpm.sock backup; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_gzip.conf: -------------------------------------------------------------------------------- 1 | #### GZip 2 | gzip on; 3 | 4 | # [OPTION] Change to increase compresssion slightly. Sacrifices CPU. 5 | gzip_comp_level 5; 6 | #gzip_comp_level 9; 7 | 8 | gzip_vary on; 9 | gzip_proxied any; 10 | gzip_min_length 256; 11 | gzip_disable "msie6"; 12 | gzip_types 13 | application/atom+xml 14 | application/geo+json 15 | application/javascript 16 | application/x-javascript 17 | application/json 18 | application/ld+json 19 | application/manifest+json 20 | application/rdf+xml 21 | application/rss+xml 22 | application/vnd.ms-fontobject 23 | application/wasm 24 | application/x-web-app-manifest+json 25 | application/xhtml+xml 26 | application/xml 27 | font/eot 28 | font/otf 29 | font/ttf 30 | image/bmp 31 | image/svg+xml 32 | text/cache-manifest 33 | text/calendar 34 | text/css 35 | text/javascript 36 | text/markdown 37 | text/plain 38 | text/xml 39 | text/vcard 40 | text/vnd.rim.location.xloc 41 | text/vtt 42 | text/x-component 43 | text/x-cross-domain-policy; 44 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_limits_20.conf: -------------------------------------------------------------------------------- 1 | limit_req zone=reqPerSec20 burst=100 nodelay; 2 | limit_conn conPerIP 20; 3 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_limits_server_2k.conf: -------------------------------------------------------------------------------- 1 | limit_conn conPerServer 2000; 2 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_logs.conf: -------------------------------------------------------------------------------- 1 | #### Logs 2 | # [OPTION] Set access log location. 3 | access_log /var/log/nginx/access.log; 4 | 5 | # [OPTION] Set error log location. 6 | error_log /var/log/nginx/error.log warn; 7 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_logs_amplify.conf: -------------------------------------------------------------------------------- 1 | #### Logs 2 | # [WARNING] log_format main_ext must be available 3 | access_log /var/log/nginx/access_amplify.log main_ext; 4 | error_log /var/log/nginx/error_amplify.log warn; 5 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_logs_off.conf: -------------------------------------------------------------------------------- 1 | #### Logs Off 2 | access_log off; 3 | error_log off; 4 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_rock-hard-ssl.conf: -------------------------------------------------------------------------------- 1 | #### SSL Stapling 2 | # [WARNING] Requires a valid `ssl_trusted_certificate` to be already set in your site file 3 | ssl_stapling on; 4 | ssl_stapling_verify on; 5 | # Google DNS, Open DNS, Dyn DNS. 6 | resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 216.146.35.35 216.146.36.36 valid=300s; 7 | resolver_timeout 3s; 8 | 9 | 10 | #### Session Tickets 11 | ssl_session_tickets on; 12 | ssl_session_timeout 24h; 13 | 14 | # [WARNING] Session Cache must be the same size in all `server` blocks. 15 | ssl_session_cache shared:SSL:100m; 16 | 17 | # [WARNING] Session Ticket Key must have been generated. 18 | ssl_session_ticket_key /etc/nginx/ssl/ticket.key; 19 | 20 | # Use a higher keepalive timeout to reduce the need for repeated handshakes 21 | keepalive_timeout 300s; # up from 75 secs default 22 | 23 | 24 | #### Diffie-Helman Parameters 25 | # [WARNING] Diffie-Helman Parameters must have been generated. 26 | ssl_dhparam /etc/nginx/ssl/dhparam3.pem; 27 | 28 | 29 | #### ECDH Curve 30 | # [OPTION] Select your preferred curve. 31 | 32 | # Option 1. [DEFAULT] Typically sufficient. 33 | ssl_ecdh_curve secp384r1; 34 | 35 | # Option 2. Slightly better. 36 | # [WARNING] Slower and breaks some IE on mobiles. 37 | #ssl_ecdh_curve secp521r1; 38 | 39 | # Option 3. Allow either on modern systems. 40 | # [WARNING] Only for for nginx >= 1.11.0 and openssl >= 1.0.2 41 | #ssl_ecdh_curve secp521r1:secp384r1; 42 | 43 | 44 | #### Cipher List 45 | # [OPTION] Select SSL protocols. 46 | # [OPTION] Pick on Cipher List from Below. 47 | # [WARNING] Breaks some browsers on some settings, many browsers on high settings. 48 | # [WARNING] TLSv1.3 requires Nginx >=1.13.0 && OpenSSL >=1.1.1 49 | # Option 1. Super-modern, probably not suitable for production, very secure. 50 | # Option 2. Modern, no XP, secure. 51 | # Option 3. [DEFAULT] Intermediate, no IE <= 6, less secure. 52 | # Option 4. Support for older browsers, not recommended unless in intranet environment. 53 | 54 | # Cipher List 55 | # https://cipherli.st 56 | # Grade A (A+ with HSTS at >= 6 Months) 57 | # 100 % Security 58 | # Low Compatibility 59 | # - No Android 2 60 | # - No Java 61 | # - No IE < 11 62 | # Robust Forward Secrecy 63 | #ssl_protocols TLSv1.3; 64 | #ssl_prefer_server_ciphers on; 65 | #ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM'; 66 | 67 | # Mozilla SSL Configuration Generator 68 | # https://mozilla.github.io/server-side-tls/ssl-config-generator/ 69 | # Nginx for Modern Browsers 70 | # Grade A (A+ with HSTS at >= 6 Months) 71 | # 90 % Security 72 | # Medium Compatibility 73 | # - No Java 6 (No DH parameters > 1024 bits) 74 | # - No IE on XP 75 | # Robust Forward Secrecy 76 | #ssl_protocols TLSv1.3; 77 | #ssl_prefer_server_ciphers off; 78 | 79 | # [DEFAULT] Mozilla SSL Configuration Generator 80 | # https://mozilla.github.io/server-side-tls/ssl-config-generator/ 81 | # Nginx for Intermediate Browsers 82 | # Grade A- 83 | # 90 % Security 84 | # High Compatibility 85 | # - No Java 6 (No DH parameters > 1024 bits) 86 | # - No IE 6 87 | # Some Forward Secrecy 88 | ssl_protocols TLSv1.2 TLSv1.3; 89 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; 90 | ssl_prefer_server_ciphers off; 91 | 92 | # Mozilla SSL Configuration Generator 93 | # https://mozilla.github.io/server-side-tls/ssl-config-generator/ 94 | # Nginx for Old Browsers 95 | # High Compatibility 96 | # NOT Recommended 97 | #ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 98 | #ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA; 99 | #ssl_prefer_server_ciphers on; 100 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_security-headers.conf: -------------------------------------------------------------------------------- 1 | #### Access Control Allow Origin 2 | # CORS: Cross-Origin Resource Sharing 3 | 4 | # [OPTION] Pick a CORS policy below. 5 | 6 | # Option 1. [DEFAULT] "origin" Only allow access to resources from origin. 7 | # [WARNING] Will stop external sites loading your assets. 8 | add_header Access-Control-Allow-Origin "origin" always; 9 | 10 | # Option 2. "*" Allow any site to load resources from your origin. 11 | #add_header Access-Control-Allow-Origin "*" always; 12 | 13 | 14 | #### Content Security Policy 15 | # There is website that helps you generate a policy here http://cspisawesome.com/ 16 | 17 | # [OPTION] Pick a Content Security Policy below. 18 | 19 | # Option 1. [DEFAULT] Off 20 | 21 | # Option 2. Report only with a few sensible defaults. 22 | # [OPTION] Configure reporting URL for report-uri.io 23 | #add_header Content-Security-Policy-Report-Only: "default-src 'self'; report-uri https://%report-uri-username%.report-uri.com/r/d/csp/reportOnly" always; 24 | #add_header X-Content-Security-Policy-Report-Only: "default-src 'self'; report-uri https://%report-uri-username%.report-uri.com/r/d/csp/reportOnly" always; 25 | #add_header X-WebKit-CSP-Report-Only: "default-src 'self'; report-uri https://%report-uri-username%.report-uri.com/r/d/csp/reportOnly" always; 26 | 27 | # Option 3. Report and enforce. 28 | # [WARNING] This is off by default for good reason. This will break lots of things if you get it wrong. 29 | # [OPTION] Configure reporting URL for report-uri.io 30 | #include directive/bubbly_security-headers_csp.conf; 31 | 32 | 33 | #### Content Type Options 34 | # Stop content loading as a different MIME Type. 35 | # [DEFAULT] NOSNIFF 36 | add_header Content-Type-Options NOSNIFF always; 37 | add_header X-Content-Type-Options NOSNIFF always; 38 | 39 | 40 | #### Expect Certificate Transparency 41 | # You can read more about this policy here https://scotthelme.co.uk/a-new-security-header-expect-ct/ 42 | 43 | # [OPTION] Pick a Expect-CT policy below. 44 | 45 | # Option 1. Off 46 | 47 | # Option 2. Report Only, no enforcement or max-age. 48 | # [OPTION] Configure reporting URL for report-uri.io 49 | #add_header Expect-CT "max-age=0, report-uri='https://%report-uri-username%.report-uri.com/r/d/ct/reportOnly'" always; 50 | 51 | # Option 3. [DEFAULT] Enforce with an age of 30 seconds to test. 52 | # [WARNING] If your site does not feature certificate transparency, this will cause your site to fail to load. 53 | add_header Expect-CT "enforce, max-age=30" always; 54 | 55 | # Option 4. Report and enforce. 56 | # [WARNING] If your site does not feature certificate transparency, this will cause your site to fail to load. 57 | # [OPTION] Configure reporting URL. Maybe use report-uri.io ? 58 | #add_header Expect-CT "enforce, max-age=31536000, report-uri='https://%report-uri-username%.report-uri.com/r/d/ct/enforce'" always; 59 | 60 | 61 | #### Frame Options 62 | # [OPTION] Stop pages being displayed in iFrames. 63 | # Option 1. [DEFAULT] DENY 64 | # [WARNING] Frame-Options and X-Frame-Options DENY will break iframed sites. 65 | # Option 2. SAMEORIGIN 66 | # Option 3. ALLOW-FROM $var 67 | add_header Frame-Options DENY always; 68 | add_header X-Frame-Options DENY always; 69 | 70 | 71 | #### Referrer Policy 72 | # Stops specific referrer information being sent to outside domains. 73 | # [OPTION] There are a lot of options for this, we suggest reading this: 74 | # https://scotthelme.co.uk/a-new-security-header-referrer-policy/ 75 | add_header Referrer-Policy "strict-origin-when-cross-origin" always; 76 | 77 | 78 | #### Server Version 79 | # [OPTION] Turn off sending the Nginx version in the server header. 80 | # Doesn't seem to work out of the box with some Nginx installations. 81 | #server_tokens off; 82 | 83 | 84 | #### Strict Transport Security 85 | # [WARNING] Strict-Transport-Security will stop HTTP access for specified time. 86 | # [OPTION] Include sub-domains with HSTS. 87 | 88 | # Option 1. [DEFAULT] Current domain only. 89 | add_header Strict-Transport-Security "max-age=31536000" always; 90 | 91 | # Option 2. includeSubDomains 92 | # [WARNING] This will block HTTP access to sub-domains. 93 | #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; 94 | 95 | 96 | #### XSS Protection 97 | # Stops pages being compromised by cross-site scripting. 98 | # Default: 1 99 | # Alternative: 0 100 | add_header XSS-Protection "1; mode=block" always; 101 | add_header X-XSS-Protection "1; mode=block" always; 102 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_security-headers_csp.conf: -------------------------------------------------------------------------------- 1 | # Generated from https://report-uri.com/home/generate 2 | 3 | add_header Content-Security-Policy: " 4 | default-src 'self'; 5 | form-action 'self'; 6 | manifest-src 'self'; 7 | img-src 'self' https://cdn.jsdelivr.net; 8 | font-src 'self' https://cdn.jsdelivr.net; 9 | style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; 10 | script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://www.google-analytics.com; 11 | child-src 'none'; 12 | frame-src 'none'; 13 | frame-ancestors 'none'; 14 | block-all-mixed-content; 15 | disown-opener; 16 | reflected-xss block; 17 | upgrade-insecure-requests; 18 | report-uri https://%report-uri-username%.report-uri.com/r/d/csp/enforce; 19 | " always; 20 | 21 | add_header X-Content-Security-Policy: " 22 | default-src 'self'; 23 | form-action 'self'; 24 | manifest-src 'self'; 25 | img-src 'self' https://cdn.jsdelivr.net; 26 | font-src 'self' https://cdn.jsdelivr.net; 27 | style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; 28 | script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://www.google-analytics.com; 29 | child-src 'none'; 30 | frame-src 'none'; 31 | frame-ancestors 'none'; 32 | block-all-mixed-content; 33 | disown-opener; 34 | reflected-xss block; 35 | upgrade-insecure-requests; 36 | report-uri https://%report-uri-username%.report-uri.com/r/d/csp/enforce; 37 | " always; 38 | 39 | add_header X-WebKit-CSP: " 40 | default-src 'self'; 41 | form-action 'self'; 42 | manifest-src 'self'; 43 | img-src 'self' https://cdn.jsdelivr.net; 44 | font-src 'self' https://cdn.jsdelivr.net; 45 | style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; 46 | script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://www.google-analytics.com; 47 | child-src 'none'; 48 | frame-src 'none'; 49 | frame-ancestors 'none'; 50 | block-all-mixed-content; 51 | disown-opener; 52 | reflected-xss block; 53 | upgrade-insecure-requests; 54 | report-uri https://%report-uri-username%.report-uri.com/r/d/csp/enforce; 55 | " always; 56 | -------------------------------------------------------------------------------- /nginx-config/directive/bubbly_uploads.conf: -------------------------------------------------------------------------------- 1 | client_max_body_size 10M; 2 | -------------------------------------------------------------------------------- /nginx-config/directive/h5bp_cache-file-descriptors.conf: -------------------------------------------------------------------------------- 1 | # src=https://github.com/h5bp/server-configs-nginx/blob/master/h5bp/web_performance/cache-file-descriptors.conf 2 | 3 | open_file_cache max=1000 inactive=20s; 4 | open_file_cache_valid 30s; 5 | open_file_cache_min_uses 2; 6 | open_file_cache_errors on; 7 | -------------------------------------------------------------------------------- /nginx-config/directive/h5bp_no-transform.conf: -------------------------------------------------------------------------------- 1 | # src=https://github.com/h5bp/server-configs-nginx/blob/master/h5bp/web_performance/content_transformation.conf 2 | 3 | add_header "Cache-Control" "no-transform"; 4 | -------------------------------------------------------------------------------- /nginx-config/directive/h5bp_x-ua-compatible.conf: -------------------------------------------------------------------------------- 1 | # src=https://raw.githubusercontent.com/h5bp/server-configs-nginx/master/h5bp/directive-only/x-ua-compatible.conf 2 | # Removed in https://github.com/h5bp/server-configs-nginx/blob/master/CHANGELOG.md#400-july-31-2021 3 | 4 | # Force the latest IE version 5 | add_header "UA-Compatible" "IE=Edge"; 6 | add_header "X-UA-Compatible" "IE=Edge"; 7 | -------------------------------------------------------------------------------- /nginx-config/groups/performance-common.conf: -------------------------------------------------------------------------------- 1 | include directive/bubbly_gzip.conf; 2 | include directive/h5bp_cache-file-descriptors.conf; 3 | include directive/h5bp_x-ua-compatible.conf; 4 | include location/h5bp_expires.conf; -------------------------------------------------------------------------------- /nginx-config/groups/security-common.conf: -------------------------------------------------------------------------------- 1 | include directive/bubbly_security-headers.conf; 2 | include directive/h5bp_no-transform.conf; 3 | include location/bubbly_well-known-passthrough.conf; 4 | include location/h5bp_protect-system-files.conf; 5 | -------------------------------------------------------------------------------- /nginx-config/location/bubbly_errors.conf: -------------------------------------------------------------------------------- 1 | # src=https://github.com/nginx-boilerplate/nginx-boilerplate/blob/master/boilerplate/locations/errors.conf 2 | 3 | fastcgi_intercept_errors on; 4 | error_page 404 /404.html; 5 | error_page 500 501 502 503 504 /50x.html; 6 | 7 | location ~ [4-5][0-9][0-9].html { 8 | internal; 9 | include directive/bubbly_logs_off.conf; 10 | } 11 | -------------------------------------------------------------------------------- /nginx-config/location/bubbly_extensionless-php.conf: -------------------------------------------------------------------------------- 1 | # Enable extensionless PHP 2 | location @extensionless-php { 3 | rewrite ^(.*)$ $1.php last; 4 | } 5 | 6 | 7 | 8 | #### PHP 9 | location ~ \.php$ { 10 | try_files $uri =404; 11 | include /etc/nginx/fastcgi_params; 12 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 13 | fastcgi_index index.php; 14 | fastcgi_keep_conn on; 15 | fastcgi_intercept_errors on; 16 | fastcgi_hide_header "Powered-By"; 17 | fastcgi_hide_header "X-Powered-By"; 18 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 19 | fastcgi_pass php_sockets; 20 | 21 | # [OPTION] Specify time to wait in seconds for PHP to respond 22 | # [DEFAULT] 10 10 60 23 | fastcgi_connect_timeout 10; 24 | fastcgi_send_timeout 10; 25 | fastcgi_read_timeout 60; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /nginx-config/location/bubbly_methods.conf: -------------------------------------------------------------------------------- 1 | if ($request_method !~ ^(OPTIONS|GET|HEAD|POST|PATCH|PUT|DELETE)$ ) { 2 | return 405; 3 | } 4 | -------------------------------------------------------------------------------- /nginx-config/location/bubbly_well-known-passthrough.conf: -------------------------------------------------------------------------------- 1 | #### Let's Encrypt Pass-Through 2 | location '/.well-known/acme-challenge' { 3 | default_type 'text/plain'; 4 | root /tmp/bubbly-authenticator; 5 | } 6 | -------------------------------------------------------------------------------- /nginx-config/location/h5bp_expires.conf: -------------------------------------------------------------------------------- 1 | expires $expires; 2 | -------------------------------------------------------------------------------- /nginx-config/location/h5bp_protect-system-files.conf: -------------------------------------------------------------------------------- 1 | # src=https://github.com/h5bp/server-configs-nginx/blob/master/h5bp/location/security_file_access.conf 2 | 3 | # Prevent clients from accessing hidden files (starting with a dot) 4 | # This is particularly important if you store .htpasswd files in the site hierarchy 5 | # Access to `/.well-known/` is allowed. 6 | location ~* /\.(?!well-known\/) { 7 | deny all; 8 | } 9 | 10 | # Block access to files that can expose sensitive information. 11 | location ~* (?:#.*#|\.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|sw[op])|~)$ { 12 | deny all; 13 | } 14 | -------------------------------------------------------------------------------- /nginx-config/mime.types: -------------------------------------------------------------------------------- 1 | # src=https://github.com/h5bp/server-configs-nginx/blob/master/mime.types 2 | 3 | types { 4 | 5 | # Data interchange 6 | 7 | application/atom+xml atom; 8 | application/json json map topojson; 9 | application/ld+json jsonld; 10 | application/rss+xml rss; 11 | # Normalize to standard type. 12 | # https://tools.ietf.org/html/rfc7946#section-12 13 | application/geo+json geojson; 14 | application/xml xml; 15 | # Normalize to standard type. 16 | # https://tools.ietf.org/html/rfc3870#section-2 17 | application/rdf+xml rdf; 18 | 19 | 20 | # JavaScript 21 | 22 | # Servers should use text/javascript for JavaScript resources. 23 | # https://html.spec.whatwg.org/multipage/scripting.html#scriptingLanguages 24 | text/javascript js mjs; 25 | application/wasm wasm; 26 | 27 | 28 | # Manifest files 29 | 30 | application/manifest+json webmanifest; 31 | application/x-web-app-manifest+json webapp; 32 | text/cache-manifest appcache; 33 | 34 | 35 | # Media files 36 | 37 | audio/midi mid midi kar; 38 | audio/mp4 aac f4a f4b m4a; 39 | audio/mpeg mp3; 40 | audio/ogg oga ogg opus; 41 | audio/x-realaudio ra; 42 | audio/x-wav wav; 43 | audio/x-matroska mka; 44 | image/apng apng; 45 | image/avif avif; 46 | image/avif-sequence avifs; 47 | image/bmp bmp; 48 | image/gif gif; 49 | image/jpeg jpeg jpg; 50 | image/jxl jxl; 51 | image/jxr jxr hdp wdp; 52 | image/png png; 53 | image/svg+xml svg svgz; 54 | image/tiff tif tiff; 55 | image/vnd.wap.wbmp wbmp; 56 | image/webp webp; 57 | image/x-jng jng; 58 | video/3gpp 3gp 3gpp; 59 | video/mp4 f4p f4v m4v mp4; 60 | video/mpeg mpeg mpg; 61 | video/ogg ogv; 62 | video/quicktime mov; 63 | video/webm webm; 64 | video/x-flv flv; 65 | video/x-mng mng; 66 | video/x-ms-asf asf asx; 67 | video/x-ms-wmv wmv; 68 | video/x-msvideo avi; 69 | video/x-matroska mkv mk3d; 70 | 71 | # Serving `.ico` image files with a different media type 72 | # prevents Internet Explorer from displaying then as images: 73 | # https://github.com/h5bp/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee 74 | 75 | image/x-icon cur ico; 76 | 77 | 78 | # Microsoft Office 79 | 80 | application/msword doc; 81 | application/vnd.ms-excel xls; 82 | application/vnd.ms-powerpoint ppt; 83 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 84 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 85 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 86 | 87 | 88 | # Web fonts 89 | 90 | font/woff woff; 91 | font/woff2 woff2; 92 | application/vnd.ms-fontobject eot; 93 | font/ttf ttf; 94 | font/collection ttc; 95 | font/otf otf; 96 | 97 | 98 | # Other 99 | 100 | application/java-archive ear jar war; 101 | application/mac-binhex40 hqx; 102 | application/octet-stream bin deb dll dmg exe img iso msi msm msp safariextz; 103 | application/pdf pdf; 104 | application/postscript ai eps ps; 105 | application/rtf rtf; 106 | application/vnd.google-earth.kml+xml kml; 107 | application/vnd.google-earth.kmz kmz; 108 | application/vnd.wap.wmlc wmlc; 109 | application/x-7z-compressed 7z; 110 | application/x-bb-appworld bbaw; 111 | application/x-bittorrent torrent; 112 | application/x-chrome-extension crx; 113 | application/x-cocoa cco; 114 | application/x-java-archive-diff jardiff; 115 | application/x-java-jnlp-file jnlp; 116 | application/x-makeself run; 117 | application/x-opera-extension oex; 118 | application/x-perl pl pm; 119 | application/x-pilot pdb prc; 120 | application/x-rar-compressed rar; 121 | application/x-redhat-package-manager rpm; 122 | application/x-sea sea; 123 | application/x-shockwave-flash swf; 124 | application/x-stuffit sit; 125 | application/x-tcl tcl tk; 126 | application/x-x509-ca-cert crt der pem; 127 | application/x-xpinstall xpi; 128 | application/xhtml+xml xhtml; 129 | application/xslt+xml xsl; 130 | application/zip zip; 131 | text/calendar ics; 132 | text/css css; 133 | text/csv csv; 134 | text/html htm html shtml; 135 | text/markdown md markdown; 136 | text/mathml mml; 137 | text/plain txt; 138 | text/vcard vcard vcf; 139 | text/vnd.rim.location.xloc xloc; 140 | text/vnd.sun.j2me.app-descriptor jad; 141 | text/vnd.wap.wml wml; 142 | text/vtt vtt; 143 | text/x-component htc; 144 | 145 | } 146 | -------------------------------------------------------------------------------- /nginx-config/sites-available/bubbly_live.conf: -------------------------------------------------------------------------------- 1 | #### Config from Bubbly: Certbot with Nginx v.2.2.0 2 | # https://github.com/eustasy/bubbly 3 | 4 | # Replace "example.com" with your domain. 5 | # Search for [WARNING] to find common warnings. 6 | # Search for [OPTION] to find options. 7 | # [DEFAULT] denotes a default option choice. 8 | 9 | 10 | 11 | #### HTTP Redirection & Verification Block 12 | # Redirect http://example.com and http://www.example.com to https://example.com 13 | server { 14 | listen 80; 15 | listen [::]:80; 16 | 17 | 18 | #### Core Configuration 19 | 20 | # [OPTION] Server Name 21 | server_name example.com www.example.com; 22 | 23 | # [OPTION] Server Name 24 | location / { 25 | return 301 https://example.com$request_uri; 26 | } 27 | 28 | 29 | include directive/bubbly_logs_off.conf; 30 | include groups/security-common.conf; 31 | } 32 | 33 | 34 | 35 | #### HTTPS Redirection Block 36 | # Redirect https://www.example.com to https://example.com 37 | server { 38 | listen 443 ssl; 39 | listen [::]:443 ssl; 40 | http2 on; 41 | 42 | 43 | #### Core Configuration 44 | 45 | # [OPTION] Server Name 46 | server_name www.example.com; 47 | 48 | # [OPTION] Server Name 49 | location / { 50 | return 301 https://example.com$request_uri; 51 | } 52 | 53 | # [OPTION] Server Name 54 | ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; 55 | ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; 56 | ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem; 57 | include directive/bubbly_rock-hard-ssl.conf; 58 | 59 | 60 | include directive/bubbly_logs_off.conf; 61 | include groups/security-common.conf; 62 | } 63 | 64 | 65 | 66 | #### Main HTTPS Block 67 | server { 68 | listen 443 ssl; 69 | listen [::]:443 ssl; 70 | http2 on; 71 | 72 | 73 | #### Core Configuration 74 | # [OPTION] Server Name 75 | server_name example.com; 76 | 77 | # [OPTION] Root Path 78 | root /path/to/example.com; 79 | 80 | # [OPTION] Index Entries 81 | index index.php index.html index.htm; 82 | 83 | # [OPTION] Automatic Indexes 84 | autoindex off; 85 | 86 | # [OPTION] Server Name 87 | ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; 88 | ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; 89 | ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem; 90 | include directive/bubbly_rock-hard-ssl.conf; 91 | 92 | 93 | charset utf-8; 94 | 95 | # Do not allow access to underscore prefixed files. 96 | location ~ /\_ { deny all; } 97 | 98 | #### Extensionless PHP 99 | include location/bubbly_extensionless-php.conf; 100 | location / { 101 | try_files $uri $uri/ @extensionless-php; 102 | } 103 | 104 | include directive/bubbly_logs_amplify.conf; 105 | include groups/security-common.conf; 106 | include groups/performance-common.conf; 107 | } 108 | -------------------------------------------------------------------------------- /nginx-config/sites-available/bubbly_verify.conf: -------------------------------------------------------------------------------- 1 | #### HTTP Initial Verification 2 | server { 3 | listen 80; 4 | listen [::]:80; 5 | 6 | #### Core Configuration 7 | # [OPTION] Server Name 8 | server_name example.com www.example.com; 9 | 10 | include directive/bubbly_logs_off.conf; 11 | include groups/security-common.conf; 12 | } 13 | -------------------------------------------------------------------------------- /screenshot_securityheaders.io.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eustasy/Bubbly/9e2fdce03d4eacc8e6c7a3e787d6913709a76e9d/screenshot_securityheaders.io.png -------------------------------------------------------------------------------- /screenshot_ssllabs.com.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eustasy/Bubbly/9e2fdce03d4eacc8e6c7a3e787d6913709a76e9d/screenshot_ssllabs.com.png --------------------------------------------------------------------------------