├── .gitignore ├── LICENSE ├── README.md ├── index.sh └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Mathias Buus 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # add-nginx-ssl 2 | 3 | Add SSL config to nginx 4 | 5 | ``` 6 | npm install -g add-nginx-ssl 7 | ``` 8 | 9 | ## Usage 10 | 11 | ``` shell 12 | # setup ssl for example.com 13 | add-nginx-ssl --key my-key.pem --cert my-cert.crt --dhparam my-dhparam.pem --domain example.com 14 | 15 | # or if you have a wildcard ssl cert 16 | add-nginx-ssl --key my-key.pem --cert my-cert.crt --dhparam my-dhparam.pem --domain *.example.com 17 | 18 | # or to just only allow ssl 19 | add-nginx-ssl --key my-key.pem --cert my-cert.crt --dhparam my-dhparam.pem --all 20 | ``` 21 | 22 | Running the above will write the SSL config to /etc/nginx/conf.d/ssl.conf and reload nginx. 23 | 24 | Protip, to generate the dhparam.pem file you can use the following command 25 | 26 | ``` shell 27 | openssl dhparam -outform pem -out dhparam2048.pem 2048 28 | ``` 29 | 30 | # Using Let's Encrypt 31 | 32 | If you don't have a certificate but have certbot installed (the command line tool for [Let's Encrypt](https://letsencrypt.org/)), add-nginx-ssl can auto issue the certificates for you, and start a auto renewel timer using systemd. 33 | 34 | ``` shell 35 | # use let's encrypt to issue and auto renew the certs 36 | add-nginx-ssl --letsencrypt -d my-domain.com -d my-other-domain.com 37 | ``` 38 | 39 | ## License 40 | 41 | MIT 42 | -------------------------------------------------------------------------------- /index.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ALL=false 6 | HELP=false 7 | DOMAINS=() 8 | LETSENCRYPT=false 9 | [ "$1" == "" ] && HELP=true 10 | 11 | if [ "$UID" != "0" ] && ! $HELP; then 12 | sudo "$0" "$@" 13 | exit $? 14 | fi 15 | 16 | # From https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility 17 | MODERN_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" 18 | LEGACY_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" 19 | UNSAFE_CIPHERS="ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP" 20 | CIPHERS="$MODERN_CIPHERS" 21 | 22 | MODERN_TLS="TLSv1.2" 23 | LEGACY_TLS="TLSv1.2 TLSv1.1 TLSv1" 24 | UNSAFE_TLS="TLSv1.2 TLSv1.1 TLSv1 SSLv3" 25 | TLS_VERSIONS="$MODERN_TLS" 26 | 27 | while true; do 28 | case "$1" in 29 | --letsencrypt) LETSENCRYPT=true; shift ;; 30 | -l) LETSENCRYPT=true; shift ;; 31 | --all) ALL=true; shift ;; 32 | -a) ALL=true; shift ;; 33 | --domain) DOMAINS+=("$2"); shift; shift ;; 34 | -d) DOMAINS+=("$2"); shift; shift ;; 35 | --help) HELP=true; shift ;; 36 | -h) HELP=true; shift ;; 37 | --key) KEY="$2"; shift; shift ;; 38 | -k) KEY="$2"; shift; shift ;; 39 | --crt) CERT="$2"; shift; shift ;; 40 | --cert) CERT="$2"; shift; shift ;; 41 | -c) CERT="$2"; shift; shift ;; 42 | --dhparam) DHPARAM="$2"; shift; shift ;; 43 | -p) DHPARAM="$2"; shift; shift ;; 44 | --modern-ciphers) CIPHERS="$MODERN_CIPHERS"; shift ;; 45 | --legacy-ciphers) CIPHERS="$LEGACY_CIPHERS"; shift ;; 46 | --unsafe-ciphers) CIPHERS="$UNSAFE_CIPHERS"; shift ;; 47 | --modern-tls) TLS_VERSIONS="$MODERN_TLS"; shift ;; 48 | --legacy-tls) TLS_VERSIONS="$LEGACY_TLS"; shift ;; 49 | --unsafe-tls) TLS_VERSIONS="$UNSAFE_TLS"; shift ;; 50 | *) break ;; 51 | esac 52 | done 53 | 54 | if $HELP; then 55 | cat <&2 76 | exit 1 77 | } 78 | 79 | check_program () { 80 | [ "$(which $1 2>/dev/null)" != "" ] && return 0 81 | error "$1 is required" 82 | } 83 | 84 | check_file () { 85 | [ -f "$1" ] && return 0 86 | error "$1 does not exist" 87 | } 88 | 89 | renew_cert_timer () { 90 | cat < /etc/systemd/system/certbot.service 91 | [Unit] 92 | Description=Lets Encrypt renewal 93 | 94 | [Service] 95 | Type=oneshot 96 | ExecStart=$(which certbot) renew --quiet --agree-tos --deploy-hook "$(which nginx) -s reload" 97 | EOF 98 | 99 | cat < /etc/systemd/system/certbot.timer 100 | [Unit] 101 | Description=Twice daily renewal of Lets Encrypt certificates 102 | 103 | [Timer] 104 | OnCalendar=0/12:00:00 105 | RandomizedDelaySec=1h 106 | Persistent=true 107 | 108 | [Install] 109 | WantedBy=timers.target 110 | EOF 111 | 112 | systemctl daemon-reload 113 | systemctl enable certbot.timer 2>/dev/null || true 114 | systemctl start certbot.timer 115 | 116 | echo "Started certificate renew timer" 117 | } 118 | 119 | setup_domains () { 120 | rm -f /etc/nginx/conf.d/ssl-domains.conf 121 | 122 | for DOMAIN in ${DOMAINS[@]}; do 123 | [ "$DOMAIN" == "-" ] && DOMAIN='*' 124 | SERVER_NAME="server_name $DOMAIN;" 125 | [ "*.${DOMAIN:2}" == "$DOMAIN" ] && WILDCARD_SERVER_NAME="server_name ${DOMAIN:2};" 126 | 127 | cat <> /etc/nginx/conf.d/ssl-domains.conf 128 | server { 129 | listen 80; 130 | $SERVER_NAME 131 | $WILDCARD_SERVER_NAME 132 | location /.well-known/acme-challenge { 133 | root /var/www/letsencrypt; 134 | } 135 | location / { 136 | return 301 https://\$host\$request_uri; 137 | } 138 | } 139 | EOF 140 | done 141 | } 142 | 143 | setup_ssl () { 144 | cat < /etc/nginx/conf.d/ssl.conf 145 | # default config (server_name _; makes this 'base' config) 146 | server { 147 | listen 443 default ssl; 148 | server_name _; 149 | 150 | location /.well-known/acme-challenge { 151 | root /var/www/letsencrypt; 152 | } 153 | 154 | ssl_certificate_key $(realpath -s "$KEY"); 155 | ssl_certificate $(realpath -s "$CERT"); 156 | 157 | # These this next block of settings came directly from the SSLMate recommend nginx configuration 158 | # Recommended security settings from https://wiki.mozilla.org/Security/Server_Side_TLS 159 | ssl_protocols $TLS_VERSIONS; 160 | ssl_ciphers '$CIPHERS'; 161 | ssl_prefer_server_ciphers on; 162 | ssl_session_timeout 5m; 163 | ssl_session_cache shared:SSL:5m; 164 | ssl_session_tickets off; 165 | 166 | # Enable this if you want HSTS (recommended) 167 | add_header Strict-Transport-Security max-age=15768000; 168 | 169 | # from https://gist.github.com/konklone/6532544 170 | # Generated by OpenSSL with the following command: 171 | # openssl dhparam -outform pem -out dhparam2048.pem 2048 172 | $SSL_DHPARAM 173 | } 174 | EOF 175 | } 176 | 177 | check_program nginx 178 | 179 | [ ! -d /etc/nginx/conf.d ] && error "/etc/nginx/conf.d does not exist. Is nginx installed?" 180 | 181 | [ "$KEY" == "" ] && ! $LETSENCRYPT && error "--key is required" 182 | [ "$CERT" == "" ] && ! $LETSENCRYPT && error "--cert is required" 183 | [ "$DOMAINS" == "" ] && ! $ALL && error "--domain or --all is required" 184 | $ALL && DOMAINS+=('-') 185 | 186 | if $ALL && $LETSENCRYPT; then 187 | error "Cannot both have --letsencrypt and --all" 188 | fi 189 | 190 | if $LETSENCRYPT; then 191 | check_program openssl 192 | check_program certbot 193 | check_program systemctl 194 | 195 | FIRST_DOMAIN="${DOMAINS[0]}" 196 | LETSENCRYPT_CERTS="/etc/letsencrypt/live/$FIRST_DOMAIN" 197 | mkdir -p /var/www/letsencrypt 198 | 199 | for DOMAIN in ${DOMAINS[@]}; do 200 | DOMAIN_LIST="$DOMAIN_LIST -d $DOMAIN" 201 | done 202 | 203 | setup_domains 204 | nginx -s reload 205 | 206 | mkdir -p /var/www/letsencrypt 207 | certbot certonly $DOMAIN_LIST --expand --webroot -n --agree-tos --register-unsafely-without-email --webroot-path /var/www/letsencrypt 208 | 209 | KEY="$LETSENCRYPT_CERTS/privkey.pem" 210 | CERT="$LETSENCRYPT_CERTS/fullchain.pem" 211 | 212 | if [ "$DHPARAM" == "" ]; then 213 | DHPARAM="$LETSENCRYPT_CERTS/dhparam2048.pem" 214 | [ ! -f "$DHPARAM" ] && openssl dhparam -outform pem -out "$DHPARAM" 2048 215 | fi 216 | fi 217 | 218 | if [ "$DHPARAM" != "" ]; then 219 | check_file "$DHPARAM" 220 | SSL_DHPARAM="ssl_dhparam $(realpath -s $DHPARAM);"; 221 | fi 222 | 223 | check_file "$KEY" 224 | check_file "$CERT" 225 | 226 | setup_domains 227 | setup_ssl 228 | nginx -s reload 229 | $LETSENCRYPT && renew_cert_timer 230 | 231 | echo "Wrote nginx SSL config to /etc/nginx/conf.d/ssl.conf and /etc/nginx/conf.d/ssl-domains.conf" 232 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "add-nginx-ssl", 3 | "version": "1.2.10", 4 | "description": "Add SSL config to nginx", 5 | "main": "index.js", 6 | "dependencies": {}, 7 | "devDependencies": {}, 8 | "bin": { 9 | "add-nginx-ssl": "./index.sh" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/mafintosh/add-nginx-ssl.git" 14 | }, 15 | "author": "Mathias Buus (@mafintosh)", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/mafintosh/add-nginx-ssl/issues" 19 | }, 20 | "homepage": "https://github.com/mafintosh/add-nginx-ssl" 21 | } 22 | --------------------------------------------------------------------------------