├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── README.md └── dhparam.pem /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## 1. Purpose 4 | 5 | A primary goal of Nginx Letsencrypt Multisite is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). 6 | 7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior. 8 | 9 | We invite all those who participate in Nginx Letsencrypt Multisite to help us create safe and positive experiences for everyone. 10 | 11 | ## 2. Open Source Citizenship 12 | 13 | A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. 14 | 15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. 16 | 17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. 18 | 19 | ## 3. Expected Behavior 20 | 21 | The following behaviors are expected and requested of all community members: 22 | 23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community. 24 | * Exercise consideration and respect in your speech and actions. 25 | * Attempt collaboration before conflict. 26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech. 27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. 28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations. 29 | 30 | ## 4. Unacceptable Behavior 31 | 32 | The following behaviors are considered harassment and are unacceptable within our community: 33 | 34 | * Violence, threats of violence or violent language directed against another person. 35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language. 36 | * Posting or displaying sexually explicit or violent material. 37 | * Posting or threatening to post other people’s personally identifying information ("doxing"). 38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. 39 | * Inappropriate photography or recording. 40 | * Inappropriate physical contact. You should have someone’s consent before touching them. 41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances. 42 | * Deliberate intimidation, stalking or following (online or in person). 43 | * Advocating for, or encouraging, any of the above behavior. 44 | * Sustained disruption of community events, including talks and presentations. 45 | 46 | ## 5. Consequences of Unacceptable Behavior 47 | 48 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated. 49 | 50 | Anyone asked to stop unacceptable behavior is expected to comply immediately. 51 | 52 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event). 53 | 54 | ## 6. Reporting Guidelines 55 | 56 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. hello@donnieashok.in. 57 | 58 | 59 | 60 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress. 61 | 62 | ## 7. Addressing Grievances 63 | 64 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Donnie with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. 65 | 66 | 67 | 68 | ## 8. Scope 69 | 70 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business. 71 | 72 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members. 73 | 74 | ## 9. Contact info 75 | 76 | hello@donnieashok.in 77 | 78 | ## 10. License and attribution 79 | 80 | This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). 81 | 82 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). 83 | 84 | Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/) 85 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Feel free to create a pull request after editing or modifying any part of it. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linode/DO + Ubuntu 16.04 LTS + UFW + Nginx (multi-site) + MySQL + phpMyAdmin + PHP 7.2 + Let's Encrypt (A+ SSL) + Cloudflare + Wordpress 2 | 3 | ## Installations 4 | 5 | ### Set up SSH key on a Linode/DO 6 | 7 | #### Linode / Digital Ocean 8 | 9 | Setup a new Linode or a Digital Ocean Droplet with Ubuntu 16.04 LTS 10 | 11 | #### Create SSH key 12 | 1. Download PuttyGen 13 | 2. Start PuTTYgen 14 | 3. Select SSH-2 RSA from the bottom menu of Type of key to generate 15 | 4. Keep number of bits: 4096 16 | 5. Click Generate button and move your mouse pointer randomly to generate random values 17 | 6. Once the process is complete you would see the public key 18 | 7. Put a key name in the key comment field 19 | 8. Save the public key to a file named key.pub 20 | 9. Save the private key to a file named key.ppk 21 | 10. Right-click on the text label showing the pub key value and save it as "authorized_keys" 22 | 23 | #### Configure SSH key 24 | 1. SSH to your Linode VPS using your root password. You can use FileZilla or Putty or both 25 | 2. Create folder `/root/.ssh` 26 | 27 | `mkdir ~/.ssh` 28 | 29 | 3. Change folder access permission 30 | 31 | `chmod 0700 ~/.ssh` 32 | 33 | 4. Upload the "authorized_keys" file to `/root/.ssh` 34 | 35 | 5. Change file access permission 36 | 37 | `chmod 0644 ~/.ssh/authorized_keys` 38 | 39 | You should now be able to login using the SSH key 40 | 41 | 6. Edit your `/etc/ssh/sshd_config` file to change 42 | 43 | `PasswordAuthentication yes` to `PasswordAuthentication no` 44 | 45 | This would disable password based login and would allow only SSH access, so make sure you have a working SSH key before doing this. 46 | 47 | 7. Hit reboot `reboot` 48 | 49 | ### Ubuntu 16.04 LTS 50 | First thing you should do after logging in via SSH is update 51 | 52 | `sudo apt-get update` 53 | 54 | if update is getting stuck then force IPv4, edit /etc/gai.conf and delete the pound (#) from this line 55 | `#precedence ::ffff:0:0/96 100` 56 | 57 | then set timezone 58 | 59 | `timedatectl set-timezone "Asia/Kolkata"` 60 | 61 | #### Install PPA repository for Nginx, Certbot & PHP 62 | `sudo apt-get install software-properties-common python-software-properties` 63 | 64 | `sudo apt-get install python3-pyasn1` 65 | 66 | `sudo add-apt-repository ppa:certbot/certbot` 67 | 68 | `sudo apt-get install python-certbot-nginx` 69 | 70 | `sudo add-apt-repository ppa:ondrej/php` 71 | 72 | `sudo add-apt-repository ppa:ondrej/nginx` 73 | 74 | `sudo apt-get update` 75 | 76 | ### Ubuntu Firewall 77 | Allow http, https and ssh access only 78 | 79 | `sudo ufw allow http` 80 | 81 | `sudo ufw allow https` 82 | 83 | `sudo ufw allow ssh` 84 | 85 | `sudo ufw enable` 86 | 87 | You will get this response 88 | 89 | `Command may disrupt existing ssh connections. Proceed with operation (y|n)?` 90 | 91 | reply with `y` 92 | 93 | Finally check status 94 | 95 | `sudo ufw status` 96 | 97 | You should get this response 98 | 99 | ``` 100 | Status: active 101 | 102 | To Action From 103 | -- ------ ---- 104 | 80 ALLOW Anywhere 105 | 443 ALLOW Anywhere 106 | 22 ALLOW Anywhere 107 | 80 (v6) ALLOW Anywhere (v6) 108 | 443 (v6) ALLOW Anywhere (v6) 109 | 22 (v6) ALLOW Anywhere (v6) 110 | ``` 111 | 112 | ### Nginx 113 | Now install Nginx 114 | 115 | `sudo apt-get install nginx` 116 | 117 | Once you are done, you can visit your IP Address on your browser and it would show: 118 | 119 | ``` 120 | Welcome to nginx! 121 | 122 | If you see this page, the nginx web server is successfully installed and working. Further configuration is required. 123 | 124 | For online documentation and support please refer to nginx.org. 125 | Commercial support is available at nginx.com. 126 | 127 | Thank you for using nginx. 128 | ``` 129 | 130 | PS: If you are using Amazon EC2, then make sure your security group allows `0.0.0.0/0` in HTTP and HTTPS 131 | 132 | At this point you are good enough to serve static html files from `/var/www/html` webroot 133 | 134 | ### MySQL 135 | Install MySQL 136 | 137 | `sudo apt-get install mysql-server` 138 | 139 | Take care to use a strong password and keep it saved somewhere else 140 | 141 | Configure secure MySQL 142 | 143 | `mysql_secure_installation` 144 | 145 | ### PHP 7 146 | Install PHP FastCGI Process Manager, additional SQL Helper Package and with other plugins like curl, mcrypt, etc. 147 | 148 | `sudo apt install php7.2 php7.2-bcmath php7.2-bz2 php7.2-cli php7.2-common php7.2-curl php7.2-fpm php7.2-gd php7.2-intl php7.2-json php7.2-mbstring php7.2-mysql php7.2-opcache php7.2-pspell php7.2-soap php7.2-tidy php7.2-xml php7.2-xmlrpc php7.2-xsl php7.2-zip` 149 | 150 | disable fix_pathinfo for better security 151 | 152 | `sudo sed -i s/\;cgi\.fix_pathinfo\s*\=\s*1/cgi.fix_pathinfo\=0/ /etc/php/7.2/fpm/php.ini` 153 | 154 | Restart PHP 155 | 156 | `sudo systemctl restart php7.2-fpm` 157 | 158 | ### phpMyAdmin 159 | `sudo apt-get update` 160 | 161 | `sudo apt-get install phpmyadmin` 162 | 163 | When asked to choose between `apache` and `lighttpd` choose none and simply press TAB key to go forward. 164 | 165 | The next prompt will ask if you would like `dbconfig-common` to configure a database for phpmyadmin to use. Select "Yes" to continue. 166 | 167 | The next prompt shall ask you the password, provide the MySQL root password. 168 | 169 | ___ 170 | 171 | ## Configurations 172 | For the purpose of this configuration part let's assume you are setting up the VPS for the first time. And the domain you are setting up is `domain.ga`. You can substitute `domain.ga` with your own domain name. 173 | 174 | ### PHP FPM Config 175 | Keep a backup of the existing www.conf file 176 | 177 | `mv /etc/php/7.2/fpm/pool.d/www.conf{,.bak}` 178 | 179 | Create a new file 180 | 181 | `/etc/php/7.2/fpm/pool.d/domain.ga.conf` 182 | 183 | and put the following in the file 184 | ``` 185 | [domain.ga] 186 | user = www-data 187 | group = www-data 188 | listen = /run/domain.ga-fpm.sock 189 | listen.owner = www-data 190 | listen.group = www-data 191 | pm = ondemand 192 | pm.process_idle_timeout = 30s 193 | pm.max_requests = 512 194 | pm.max_children = 30 195 | ``` 196 | 197 | Issue a PHP restart: 198 | 199 | `service php7.2-fpm restart` 200 | 201 | ### PHP Web root 202 | 203 | Create the root folder for the domain (assuming domain.ga) 204 | 205 | `mkdir -p /var/www/domain.ga/{files,log}` 206 | 207 | Inside `files` add `index.php` with a line of code 208 | 209 | `echo "> /var/www/domain.ga/files/index.php` 210 | 211 | inside the `log` folder create blank files 212 | 213 | `touch access.log error.log` 214 | 215 | ### Cloudflare 216 | Add your domain name to Cloudflare and enable the DNS proxy 217 | 218 | ### Nginx Server Blocks 219 | #### Nginx configuration snippets 220 | write `/etc/nginx/sites-available/domain.ga` 221 | 222 | ``` 223 | server { 224 | listen 80; 225 | server_name domain.ga; 226 | root /var/www/domain.ga/files; 227 | index index.php index.html index.htm; 228 | 229 | access_log /var/www/domain.ga/log/access.log; 230 | error_log /var/www/domain.ga/log/error.log notice; 231 | 232 | location ~ \.php$ { 233 | try_files $uri =404; 234 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 235 | include fastcgi_params; 236 | fastcgi_read_timeout 300; 237 | fastcgi_intercept_errors on; 238 | fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; 239 | fastcgi_pass unix:/run/domain.ga-fpm.sock; 240 | } 241 | } 242 | ``` 243 | 244 | create a shortcut of the file from `/etc/nginx/sites-enabled` 245 | 246 | `ln -s /etc/nginx/sites-available/domain.ga /etc/nginx/sites-enabled/` 247 | 248 | Reload Nginx 249 | 250 | `service nginx restart` 251 | 252 | At this point you are ready to talk PHP on domain.ga, visit domain.ga to make sure you see the 'Hello World' message 253 | 254 | ### phpMyAdmin web link 255 | 256 | create a shortcut of phpMyAdmin in the root folder of your domain 257 | 258 | `ln -s /usr/share/phpmyadmin/ /var/www/domain.ga/files` 259 | 260 | Now you can access phpMyAdmin on domain.ga/phpmyadmin/ 261 | 262 | ### Let's Encrypt 263 | #### Let's Encrypt Certificate installation 264 | Install certificates by --webroot plugin 265 | 266 | ``` 267 | sudo certbot certonly --webroot -w /var/www/domain.ga/files/ -d domain.ga 268 | ``` 269 | 270 | #### Create a strong Diffie-Hellman parameter 271 | 272 | `openssl dhparam -out /etc/nginx/dhparam.pem 4096` 273 | 274 | This will take sometime, go and grab a coffee. 275 | OR 276 | You can use the dhparam.pem file available with this repo and put it in the nginx directory. 277 | 278 | #### Append Nginx SSL Server Block 279 | Open `/etc/nginx/sites-available/domain.ga` 280 | 281 | And append the below lines after the first server block 282 | 283 | ``` 284 | server { 285 | listen 443 ssl; 286 | server_name domain.ga; 287 | root /var/www/domain.ga/files; 288 | index index.php index.html index.htm; 289 | 290 | access_log /var/www/domain.ga/log/access.log; 291 | error_log /var/www/domain.ga/log/error.log notice; 292 | 293 | ssl_certificate /etc/letsencrypt/live/domain.ga/fullchain.pem; 294 | ssl_certificate_key /etc/letsencrypt/live/domain.ga/privkey.pem; 295 | 296 | location ~ \.php$ { 297 | try_files $uri =404; 298 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 299 | include fastcgi_params; 300 | fastcgi_read_timeout 300; 301 | fastcgi_intercept_errors on; 302 | fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; 303 | fastcgi_pass unix:/run/domain.ga-fpm.sock; 304 | } 305 | } 306 | ``` 307 | 308 | open nginx.conf and replace the SSL section with this: 309 | 310 | ``` 311 | ssl_dhparam /etc/nginx/dhparam.pem; 312 | ssl_protocols TLSv1.2; 313 | ssl_prefer_server_ciphers on; 314 | ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; 315 | ssl_ecdh_curve secp384r1; 316 | ssl_session_timeout 10m; 317 | ssl_session_cache shared:SSL:10m; 318 | ssl_session_tickets off; 319 | ssl_stapling on; 320 | ssl_stapling_verify on; 321 | resolver 8.8.8.8 8.8.4.4 valid=300s; 322 | resolver_timeout 5s; 323 | add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; 324 | add_header X-Frame-Options DENY; 325 | add_header X-Content-Type-Options nosniff; 326 | add_header X-XSS-Protection "1; mode=block"; 327 | ``` 328 | 329 | Check Nginx configurations 330 | 331 | `nginx -t` 332 | 333 | You should see this: 334 | 335 | ``` 336 | nginx: the configuration file /etc/nginx/nginx.conf syntax is ok 337 | nginx: configuration file /etc/nginx/nginx.conf test is successful 338 | ``` 339 | 340 | Reload Nginx 341 | 342 | `nginx -s reload` 343 | 344 | At this point you can access https://domain.ga and see `Hello World` on it. 345 | 346 | You should also check if you are getting an A+ SSL rating 347 | Visit https://www.ssllabs.com/ssltest/analyze.html?d=domain.ga after disabling DNS proxy on Cloudflare 348 | 349 | #### Auto-renew using crontab: 350 | 351 | `sudo crontab -e` 352 | 353 | Add the following lines to renew certs every 1 and 15th day of the month at 2:00am 354 | 355 | `00 2 1 * * /usr/bin/certbot renew -q 356 | 00 2 15 * * /usr/bin/certbot renew -q` 357 | 358 | ### Wordpress (Optional) 359 | #### Configure Nginx server block 360 | 361 | Replace try_files directive with this 362 | 363 | `try_files $uri $uri/ /index.php?q=$uri&$args;` 364 | 365 | For additional security add this location block to deny access to php files 366 | 367 | ``` 368 | # SECURITY : Deny all attempts to access PHP Files in the uploads directory 369 | location ~* /(?:uploads|files)/.*\.php$ { 370 | deny all; 371 | } 372 | location ~ /\.ht { 373 | deny all; 374 | } 375 | ``` 376 | 377 | This one is useful for sitemap links 378 | 379 | ``` 380 | # PLUGINS : Enable Rewrite Rules for Yoast SEO SiteMap 381 | rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last; 382 | rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last; 383 | ``` 384 | 385 | #### Setup MySQL Database with User 386 | Login to MySQL 387 | 388 | `mysql -u root -p` 389 | 390 | Create user 391 | 392 | `CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';` 393 | 394 | Create new database 395 | 396 | `CREATE DATABASE domain_blog;` 397 | 398 | Grant privileges 399 | 400 | `GRANT ALL PRIVILEGES ON domain_blog. * TO 'newuser'@'localhost' IDENTIFIED BY 'password';` 401 | 402 | Reload privileges 403 | 404 | `FLUSH PRIVILEGES;` 405 | 406 | exit mysql 407 | 408 | `exit` 409 | 410 | #### Get latest Wordpress version 411 | 412 | move to your files directory 413 | 414 | `cd /var/www/domain.ga/files` 415 | 416 | download the latest version of Wordpress 417 | 418 | `wget "https://wordpress.org/latest.zip";` 419 | 420 | Unzip it 421 | 422 | `unzip latest.zip` 423 | 424 | and move the contents to the files directory 425 | 426 | `mv -a /var/www/domain.ga/files/wordpress/* /var/www/domain.ga/files/` 427 | 428 | adjust file ownership settings 429 | 430 | `sudo chown -R www-data:www-data /var/www/domain.ga/files/` 431 | 432 | set file group inheritance 433 | 434 | `sudo find /var/www/domain.ga/files/ -type d -exec chmod g+s {} \;` 435 | 436 | At this point you can visit your domain https://domain.ga and finish the Wordpress installation with the correct database credentials 437 | 438 | ### Adding new sites 439 | 1. Create folder in /var/www/ with files and log directory 440 | 2. Add PHP-FPM config file into /etc/php/7.2/fpm/pool.d for the new site 441 | 3. Add the server block to /etc/nginx/sites-available and create shortcut of it on sites-enabled 442 | 4. Restart PHP 443 | 5. Restart Nginx 444 | 6. Get an SSL certificate from Let's Encrypt 445 | 7. Add SSL server block to server configuration file in /etc/nginx/sites-available/ 446 | 8. Restart Nginx 447 | 9. Add site to cloudflare 448 | 10. Edit wp-config.php file and connect with database 449 | 11. rename Facebook plugin folder if there is an error with Wordpress, it's known to create troubles while shifting hosts 450 | 451 | -------------------------------------------------------------------------------- /dhparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIICCAKCAgEApFPvfYTqaFFctEcnBiZh9sXs6k32Jva+/MhmgWqJ4bOF6EDiY3tF 3 | h4a57ktzEyEbkeoULDni8MGyqtD0REA5uA8d9YMA83A+w91DqPnRtM6vGAzGIXuN 4 | lvMODQtpHJMbOCACLirJlwpSeKTqzGvYmXmLtP59ZBJsf6Aun0gpvTlYqN+nbnVt 5 | fRQsA7jTAadB+kYB7olVQar4DUQLKO1wm4Up/5pNt7ui3GLex7/+Vf1K50vfJiX3 6 | 0mRRYO7gCC6g55nh089LxQ9wuIA8nFJouS+0+vBu02oFHNiukGr421NC5jVYRvH3 7 | uZiVitq4A9PzNgg2g8d8/EXSbg7C1bUuF1JQQ63CxJirhxH2+YvzrAODUcBhzDGr 8 | rSaTG8y4yujezgRhu05JNTwbfRs3toOLEncZ3lagKQj5cWJIBisJpFwNCAlBcVfM 9 | AhXJ5ldF8P4+nJoOmtoZ8I7PF5LwrDjPdk4jkYJGDiA/w8oyqhzouh1D3dQBtdZd 10 | 2kPBfBsl7stY2sbUl4wzzMgPuaBxDc+aJl6EkjSjz8IrVP2jyzx9JSyT77KDcxZW 11 | 5o9JrDrRuWxqnByHrP9osOe2WexgEEInnsW/JT+AG/FlPCA+ieXA5NWKqd7+Jpiy 12 | ZoIwzxyAmsYTTsP+aB9nl2GaFKQN+kcxSIxVXHVRMAuLAqrNPNQhpnMCAQI= 13 | -----END DH PARAMETERS----- 14 | --------------------------------------------------------------------------------