How to set up your own email server using AWS

https://speedkills.io/email-server-aws/


Introduction: SaaS vs Hosted

When companies think about getting their emails under their own domains, they often ask the same question: should we use an email service like Gmail (Google Apps for Business) or set up our own email server? There are pros and cons to both options, so let me try and sum them up.

Maintenance

When working with SaaS solutions, you outsource all the maintenance burden like spam-filtering, software updates, new feature developments, etc., to the service provider. This means that you do not need to keep an in-house system administrator to handle these tasks.
Typical maintenance list for a mail server consists of:
  • Monitoring server space and health in general
  • Monitoring email bounces and complaints
  • Removing the server from blacklists if it gets into any
  • Software updates
  • User management
  • Reviewing and updating spam-detection rules
Having your own server, however, gives you more flexibility:
  • You can implement any feature without waiting for a 3rd party developer to do it for you
  • Any new features, new mailboxes and domains, do not add anything to your monthly bill for the email server
  • You have full access to the system, so in case of any issues you do not have a long feedback loop and do not need to fight your way through several levels of support to get your problem solved

Security and Privacy

When you store your emails on your own server you control, you know who reads them and when. You can rest assured that none of your emails are being scanned by crawlers for keywords to use in ad campaigns. Your information is available only to you.
When you entrusting your emails to a 3rd party company, you entrust them with basically all of your secrets. Even though the likelihood of a 3rd party company’s employee reading through your emails is small, your emails will, at the least, be scanned and crawled for keywords in order to server better-targeted ads.

End-user support

The tricky part of any email service is that more often than not the problem is actually located on the client’s computer. Any 3rd party support team at this point will let you deal with the problem on your own; supporting end-customers is not (and cannot be) their responsibility.
An in-house administrator, on the other hand, will be able to come over to an employee’s desk and solve the problem. This can potentially save you days of an employee’s time, compared to talking to the remote support team of a SaaS email service. (if they even have one; Gmail doesn’t).

Total Cost of Ownership (or, “TCO”)

When calculating the total cost of your email service, don’t think just about monthly costs per user.
Start with answering a few questions about the role of email in your company, such as:
Is it a business-critical service for you?
Some companies move away from emails to ticket and project management tools like Basecamp or JIRA, some move to messengers like Slack or Flowdock. In this case, email becomes nothing more but a bucket for notifications.
Will your business suffer any monetary losses if your email becomes unavailable for an hour?
What about a day? Two days? Try to roughly estimate the impact of such a situation.
Do you have a person who is technical enough to set up and maintain the email server and support workstations of your employees?
If yes - does that person have enough free time after his/her other routine tasks or will it be an overtime, extra duty?
Do you have a server available for hosting an email service or would you need to buy/rent one?
Add server rent/purchase costs, as well as utility bills (if applicable) to the equation.
Think about your employee profile.
Can you foresee who / how many team members are less technical and are likely to have questions and require support every now and then? This would add both direct costs in support administrator time and indirect costs in your employees being idle while their question is being answered and their problem solved.
***
All in all, TCO should reflect both your risks and your direct expenses split by month.
Take into account that monthly maintenance expenses on an on-premises email server after the initial setup are going to decrease as the solution will become stable and only occasional fine-tuning will be required. SaaS providers, in turn, will not ask for huge investments from the start, but the monthly fee will not go down over time either.

Create your AWS instance

For simplicity, this tutorial will assume that you are using Amazon Web Services (AWS) as the hosting platform for your email server. However, it’s possible to set up a similar mail server on any other dedicated server if you have full root access to it.
To create an AWS instance, log it to your AWS account and go to EC2 here: https://console.aws.amazon.com/ec2/v2/home
In the top-right corner, select a region. It is always wise to place the instance as close as possible to the user, so if the client is located on the East Coast, pick U.S. East (N.Virginia):
Next, go to Instances and click Launch Instance:
This will open a pop-up that will take you through instance creation process. Select Ubuntu 16.04 as the base image:
Instance type on the second screen - select t2.micro (should be selected by default):
Click Next: Configure Instance Details.
You do not need to change anything on the following screen, just click Next: Add Storage.
Here, change the primary partition size to 12GB:
Click Add New Volume and set it’s size to at least 50GB:
Click Next: Add Tags
Click Add Tag and put Key: Name, Value: your mail server URL:
This step is optional but helps a lot to improve maintainability of your AWS account.
Click Next: Configure Security Group
If you are using a new AWS account, you will need to configure a security group as seen on the screenshot:
It will open the mail server to the world and will grant you (and only you) access to the server via SSH. If you will ever need to access the server from a different IP address, you will have to update the security group.
!!! NB: never leave SSH port open to the world (0.0.0.0/0) as it will lead to a major security breach!
In the Vic’s account security groups are already configured, so just use them. There are two security groups  that must be attached to the instance:
Click Review and Launch.
On the next screen, click Launch.
In the following pop-up, select the existing private key if you have it or create a new one.
You will need this key to connect to the instance and configure the server.

Attach Elastic IP address

You will see your new instance there in “pending” state. Wait until it changes to “running” - it may take a couple of minutes.
Once it is in “running” state, go to Elastic IPs section: https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#Addresses:sort=PublicIp and click “Allocate new address” button.
In the following screen, make sure “VPC” is selected and click “Allocate”, then “Close”:
Next, right-click on the new IP address and choose “Associate address”:
Select your instance from the “Instance” drop-down and click “Associate”:
Now you are able to connect to your instance using this IP address and the IP will remain always associated with your instance. This IP address must be used for all mail-related DNS records as well.
Now you are ready to connect to your new Linux server! Use username “ubuntu” and the key you selected/generated during instance creation process, for example: ssh ubuntu@34.233.157.156 -i viktors.pem

Create DNS records

To start, you will need to point the following records to your instance IP address (A records):
  1. mail.yourdomainname.com - this will be used by the mail server and mail clients to send/receive emails
  2. webmail.yourdomainname.com - this domain will be used for webmail interface (Roundcube)
  3. vim.yourdomainname.com - here we will host ViMbAdmin, which is responsible for accounts, aliases and groups management
Also, you will need to add an MX record:

Configure instance

Before setting up the mail server, there are a few steps you need to take to configure general server settings.

Configure partition

By default the second drive you created (50GB) will not be mounted and available - you need to create a partition and mount it first.
  1. Launch fdisk for the second drive
$ sudo fdisk /dev/xvdb
  1. Hit n to create new partition, then p for primary partition. Leave the rest default (Enter-Enter-Enter)
  1. Hit w to write changes to disk and exit
  1. Now that you have created a partition, it is time to create a filesystem on it:
$ sudo mkfs.ext4 /dev/xvdb1
  1. Create a mountpoint
$ sudo mkdir /var/vmail/
  1. Mount the partition:
$ sudo bash -c ‘echo “/dev/xvdb1 /var/vmail/ ext4 defaults 0 0” >> /etc/fstab’
$ sudo mount /var/vmail
$ sudo rm -rf /var/vmail/lost+found

Create swap file

EC2 instances do not have a swap file by default - let’s create one to improve system stability. This will create a 4GB swapfile in /var/swapfile and mount it:
$ sudo dd if=/dev/zero of=/var/swapfile bs=1M count=4096
$ sudo chmod 600 /var/swapfile
$ sudo mkswap /var/swapfile
$ sudo bash -c ‘echo “/var/swapfile /dev/null swap defaults 0 2” >> /etc/fstab’
$ sudo swapon -a

Install packages

  1. Update package information:
    $ sudo apt-get update
  1. Install nginx and letsencrypt
$ sudo apt-get install nginx letsencrypt

Configure nginx

Create dhparam file:
$ sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
Replace contents of /etc/nginx/nginx.conf with the following: https://pastebin.com/iCddfxJC
And delete default vhost configuration file: /etc/nginx/sites-enabled/default
Lastly, restart nginx:
$ sudo service nginx restart

Configure letsencrypt

Letsencrypt allows us to obtain and renew a free fully-functional SSL certificate. Letsencrypt certificates have 3 months TTL so they must be renewed quite often.

Obtain SSL certificates

$ sudo letsencrypt certonly –webroot -w /tmp -d mail.yourdomainname.com
$ sudo letsencrypt certonly –webroot -w /tmp -d webmail.yourdomainname.com
$ sudo letsencrypt certonly –webroot -w /tmp -d vim.yourdomainname.com

Automatically renew certificates

To avoid any downtime and extra maintenance, add cron job to automatically renew certificates and reload all affected services:
$ sudo crontab -e
Add the following record to root’s crontab:
## This will renew all SSL certificates once a week and reload nginx, postfix and dovecot
0 3 * * 2 /usr/bin/letsencrypt renew; systemctl restart postfix; systemctl restart dovecot; systemctl reload nginx

Install and configure Postfix

Install packages

$ sudo apt-get install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql mysql-server dovecot-sieve dovecot-managesieved
Depending on previously installed package list, you may or may not see prompts to configure mysql, postfix, etc.
You will be prompted to enter and confirm mysql root password. Use password generator to create a safe 16+ character password and note it down.
Next, you will need to choose Postfix configuration type. Select “Internet site”.
The next prompt will ask for the correct mail name. Enter the domain name you are configuring the mail server for.

Create a mysql database and user for vimbadmin

Log in to mysql using:
$ mysql -uroot -p
And enter mysql root password when prompted.
Then, run the following SQL:
CREATE DATABASE ‘vimbadmin’;
GRANT ALL ON ‘vimbadmin’.* TO ‘vimbadmin’@’127.0.0.1’ IDENTIFIED BY ‘password’;
FLUSH PRIVILEGES;

Configure Postfix

Navigate to /etc/postfix and edit master.cf configuration file:
$ cd /etc/postfix
$ sudo nano master.cf
There, find and uncomment the following two lines:
submission inet n       -       -       -       -       smtpd
smtps     inet  n       -       -       -       -       smtpd
Next, open main.cf and add the following lines towards the end of the file. Don’t forget to update the paths for tls cert and key files!
# Change postfix TLS parameter to use dovecot
#smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.yourdomain.com/privkey.pem
smtpd_use_tls=yes
#smtpd_tls_auth_only = yes
#Handle SMTP authentication using Dovecot
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions =
       permit_sasl_authenticated,
       permit_mynetworks,
       reject_unauth_destination
# other destination domains should be handled using virtual domains
mydestination = localhost
# using Dovecot’s LMTP for mail delivery and giving it path to store mail
virtual_transport = lmtp:unix:private/dovecot-lmtp
# virtual mailbox setups
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_alias_maps = mysql:/etc/postfix/mysql/virtual_alias_maps.cf
virtual_mailbox_domains = mysql:/etc/postfix/mysql/virtual_domains_maps.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql/virtual_mailbox_maps.cf
Now, let’s create configuration to store all mailbox-related information in mysql:
$ sudo mkdir /etc/postfix/mysql
Create a file /etc/postfix/mysql/virtual_alias_maps.cf
$ sudo nano /etc/postfix/mysql/virtual_alias_maps.cf
And paste the following in it (don’t forget to put the correct mysql credentials in!):
user = user
password = password
hosts = 127.0.0.1
dbname = vimbadmin
query = SELECT goto FROM alias WHERE address = ‘%s’ AND active = ‘1’
Next, create /etc/postfix/mysql/virtual_domains_maps.cf
$ sudo nano /etc/postfix/mysql/virtual_domains_maps.cf
And paste the following:
user = user
password = password
hosts = 127.0.0.1
dbname = vimbadmin
query = SELECT domain FROM domain WHERE domain = ‘%s’ AND backupmx = ‘0’ AND active = ‘1’
Lastly, create /etc/postfix/mysql/virtual_mailbox_maps.cf
$ sudo nano /etc/postfix/mysql/virtual_mailbox_maps.cf
And paste the following:
user = user
password = password
hosts = 127.0.0.1
dbname = vimbadmin
query = SELECT maildir FROM mailbox WHERE username = ‘%s’ AND active = ‘1’

Dovecot configuration

Dovecot is an IMAP and POP server. It also implements security/authentication for IMAP/POP as well as SMTP (via Postfix).
Create Linux system user that will own all email in the system
$ sudo groupadd -g 5000 vmail
$ sudo useradd -g vmail -u 5000 vmail -d /var/vmail -m
Restart postfix
$ sudo service postfix restart

Enable required protocols

Edit /etc/dovecot/dovecot.conf
$ sudo nano /etc/dovecot/dovecot.conf
And enable required protocols (add the protocols = … line):
# Enable installed protocols
!include_try /usr/share/dovecot/protocols.d/*.protocol
protocols = imap pop3 lmtp sieve

Configure mail storage location

Edit /etc/dovecot/conf.d/10-mail.conf
$ sudo nano /etc/dovecot/conf.d/10-mail.conf
And set mail_location to:
mail_location = maildir:/var/vmail/%d/%n

Configure authentication

Edit /etc/dovecot/conf.d/10-auth.conf and make sure that the following options are set to:
disable_plaintext_auth = no
auth_mechanisms = plain login
Also comment out line: !include auth-system.conf.ext to disable system user authentication.
Add the following lines at the end of the file:
passdb {
   driver = sql
   args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
   driver = static
   args = uid=5000 gid=5000 home=/var/vmail/%d/%n allow_all_users=yes
}

Configure mysql parameters in dovecot

Edit /etc/dovecot/dovecot-sql.conf.ext and paste the following at the bottom:
driver = mysql
connect = host=127.0.0.1 dbname=vimbadmin user=user password=password
password_query = \
 SELECT username AS user, password, \
   homedir AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \
 FROM mailbox WHERE username = ‘%u’
iterate_query = SELECT username AS user FROM mailbox

Change master config file

Edit /etc/dovecot/conf.d/10-master.conf and make sure it looks like this:
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
  mode = 0600
  user = postfix
  group = postfix
 }
}
service auth {
unix_listener /var/spool/postfix/private/auth {
   mode = 0666
   user = postfix
   group = postfix
 }
 unix_listener auth-userdb {
   mode = 0600
   user = vmail
 }
 user = dovecot
}
service auth-worker {
 user = vmail
}

Configure Logging

Edit /etc/dovecot/conf.d/10-logging.conf and set
log_path = /var/log/dovecot.log

Restart Dovecot

$ sudo service dovecot restart

Install and configure ViMbAdmin

Install PHP and composer

For Ubuntu 16.04 default PHP version is 7.0.x:

Install packages

$ sudo apt-get install php7.0-fpm php7.0-mcrypt php-memcache php7.0-json php7.0-mysql php-gettext php7.0-zip
$ sudo curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

Set time zone in PHP

Edit /etc/php/7.0/fpm/php.ini
$ sudo nano /etc/php/7.0/fpm/php.ini
And set timezone: date.timezone = UTC
Then, restart php:
$ sudo service php7.0-fpm restart

Install ViMbAdmin

$ cd /usr/local
$ sudo git clone git://github.com/opensolutions/ViMbAdmin.git vimbadmin
$ sudo git checkout 3.0.15
$ cd /usr/local/vimbadmin
$ sudo composer install
$ sudo chown -R www-data: /usr/local/vimbadmin

Edit vimbadmin config file

$ cd /usr/local/vimbadmin
$ sudo cp application/configs/application.ini.dist application/configs/application.ini
$ sudo nano application/configs/application.ini
And update the contents:
securitysalt = “superadmin-password”  ← Generate super-secure password here
defaults.mailbox.uid = 5000
defaults.mailbox.gid = 5000
defaults.mailbox.homedir = “/var/vmail/”
resources.doctrine2.connection.options.driver   = ‘pdo_mysql’
resources.doctrine2.connection.options.dbname   = ‘vimbadmin’  ← Don’t forget to update this!
resources.doctrine2.connection.options.user     = ‘vimbadmin’  ← Don’t forget to update this!
resources.doctrine2.connection.options.password = ‘password’ ← Don’t forget to update this!
resources.doctrine2.connection.options.host     = ‘localhost’

Create mysql tables

$ cd /usr/local/vimbadmin
$ sudo mv public/.htaccess.dist .htaccess
$ sudo ./bin/doctrine2-cli.php orm:schema-tool:create
$ sudo chown -R www-data:www-data /usr/local/vimbadmin

Configure nginx

Create file /etc/nginx/sites-enabled/vma.yourdomain.com and paste the following:
server {
listen 443 ssl;
server_name vma.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/vma.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vma.yourdomain.com/privkey.pem;
access_log   /var/log/nginx/vma.yourdomain.com.access.log;
error_log    /var/log/nginx/vma.yourdomain.com.error.log;
root /usr/local/vimbadmin/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
               fastcgi_split_path_info ^(.+\.php)(/.+)$;
               fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
}
}

Install and configure RoundCube

RoundCube is webmail interface. Since email can be checked using any email client, this part is optional but recommended for convenience.

Install packages

$ sudo apt-get install roundcube roundcube-plugins roundcube-plugins-extra
Answer “Yes” to “Configure database for roundcube with dbconfig-common? ” prompt.

Configure nginx

Create /etc/nginx/sites-enabled/webmail.yourdomain.com and paste there the following:
server {
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/webmail.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/webmail.yourdomain.com/privkey.pem;
server_name webmail.yourdomain.com;
access_log   /var/log/nginx/webmail.yourdomain.com.access.log;
error_log    /var/log/nginx/webmail.yourdomain.com.error.log;
root /usr/share/roundcube;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
}
}
Restart nginx:
$ sudo service nginx restart

How to ssh from one ec2 instance to another?

Source:
https://superuser.com/questions/1135766/how-to-ssh-from-one-ec2-instance-to-another
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules-reference.html


Rules to connect to instances from an instance with the same security group

To allow instances that are associated with the same security group to communicate with each other, you must explicitly add rules for this.
The following table describes the inbound rule for a security group that enables associated instances to communicate with each other. The rule allows all types of traffic.
Protocol typeProtocol numberPortsSource IP
-1 (All)-1 (All)-1 (All)The ID of the security group
Method 1 - use the same keys on the servers:
Convert the keys to openssh format and upload the private keys to the servers. When you ssh to the destination host, specify the private key file:
ssh -i mykey.pem private.ip.of.other.server

Method 2 - Create new keys
On each server run:
ssh-keygen
Hit enter enter enter. You'll have two files:
.ssh/id_rsa
.ssh/id_rsa.pub
On Server A, cat and copy to clipboard the public key:
cat ~/.ssh/id_rsa.pub
[select and copy to your clipboard]
ssh into Server B, and append the contents of that to the it's authorized_keys file:
cat >> ~/.ssh/authorized_keys
[paste your clipboard contents]
[ctrl+d to exit]
Now ssh from server A:
ssh -i ~/.ssh/id_rsa private.ip.of.other.server

How to allow all connections from specific IP address?

Source: https://serverfault.com/questions/857190/how-to-allow-all-connections-from-specific-ip-address


How to properly allow all connections from specific IP address?
"All connections" means don't match tcp on your specific IP allow rule!
Just remove -p tcp and you'll have ping (ICMP) and all the other IP protocol stuff allowed.
Also please note that the OUTPUT rule here is not useful. With the first rule you allow tcp to 192.168.0.45, with the second one you allow anything to anybody. So the first one has no effect!
In order to get what you want edit the input rule:
iptables -A INPUT -s 192.168.0.45/32 -j ACCEPT
And remove the OUTPUT rule you made for 192.168.0.45.

CSS: Change another element on hover

Source: https://jsfiddle.net/ThinkingStiff/dWHzF/

.sibling-hover, #parent {
    cursor: pointer;
}
.sibling-hover:hover ~ .sibling-highlight {
    background-color: red;
    color: white;
}
#parent:hover .parent-highlight {
    background-color: red;
    color: white;
}

<b>sibling method:</b>
<div>
    <div class="sibling-hover">hover over me</div>
    <div>I do nothing</div>
    <div>I do nothing</div>
    <div class="sibling-highlight">I get highlighted</div>
</div>
<b>child method:</b>
<div id="parent">
    <div>hover over me</div>
    <div>hover over me</div>
    <div>hover over me</div>
    <div class="parent-highlight">I get highlighted</div>
</div>

Apache Redirect

https://serverfault.com/questions/344614/simple-apache2-redirect-from-one-domain-to-another

https://www.digitalocean.com/community/questions/how-to-redirect-https-ip-to-https-domain

https://stackoverflow.com/questions/33874212/htaccess-redirect-https-to-another-https

You cannot match scheme http:// in RewriteCond %{HTTP_HOST}.
You can use this rule:
RewriteEngine On

RewriteCond %{HTTP_HOST} !^www\.domain\.com$ [NC]
RewriteRule ^ https://www.domain.com%{REQUEST_URI} [R=302,L]
You can use the RedirectPermanent directive to redirect the client to your new URL.
Just create a very simple VirtualHost for the old domain in which you redirect it to the new domain:
<VirtualHost *:80>
    ServerName xy.example.com
    RedirectPermanent / http://abc.example.com/
    # optionally add an AccessLog directive for
    # logging the requests and do some statistics
</VirtualHost>

Sass: Install and Execute

Source: https://sass-lang.com/install

When you install Sass on the command line, you'll be able to run the sass executable to compile .sass and .scss files to .css files. For example:
sass source/stylesheets/index.scss build/stylesheets/index.css

Install Anywhere (npm)
If you use Node.js, you can also install Sass using npm by running
npm install -g sass

What is a CSS Source Map?

Source: https://www.sitepoint.com/using-source-maps-debug-sass-chrome/

A source map is a .map file in JSON format that gets generated when your Sass files are compiled into CSS. It contains information that links each line of your CSS file (the output) to the corresponding line in its SCSS source file (the input).
This means when your CSS is compiled, you’ll see a comment like the following at the end of your compiled CSS file:
/*# sourceMappingURL=style.css.map */
Below is the content of a truncated .map file.
{
"version": 3,
"mappings": ";AAiIA,qDAAsD;EACpD,OAAO,EAAE,GAAG"
"sources": ["../scss/foundation/components/_breadcrumbs.scss","../scss/foundation/components/_icon-bar.scss","../scss/foundation/components/_offcanvas.scss","../scss/foundation/components/_visibility.scss","../scss/foundation/components/_global.scss","../scss/foundation/pages/_index.scss"],
"names": [],
"file": "foundation.css"
}

How Do Source Maps Help?

When debugging a web page using Chrome’s developer tools using source maps, the styles panel will show which Sass partial your CSS came from instead of pointing to the CSS file. Meaning, instead of showing the source as style.css, it will show it as _buttons.scss.
In the example in the screenshot below, the styles for the body element come from a partial called _body.scss.
Screenshot of Dev tools
When inspecting, I would like to know where I have defined the color: black portion in this.
To get this information, I could perform one of the following actions to inspect further:
  1. Press CTRL and click on the word color. It will open the appropriate partial in the sources tab and place the cursor in the exact line where the style is defined. In this case, the color is defined in a variable called $black.
  2. Press CTRL and click on the word black. It will open _colors.scss in the sources tab, where the value for the variable $black is defined. In case the color black was coming from a mixin instead of a variable, CTRL + clicking on it will open the file where the mixin is defined. Click the image below to view an animated GIF that shows a demonstration of how this might look.
Inspecting and live editing Sass in the browser
Using source maps with Workspaces allows you to edit and save changes from within Chrome’s developer tools. If sass --watch is running, it will detect the change and recompile the CSS. The browser will then automatically reload this new CSS.

How to enable Gzip compression

Source: https://varvy.com/pagespeed/enable-compression.html
https://knackforge.com/blog/karalmax/how-enable-gzip-compression-apache

The code below should be added to your .htaccess file...
<ifModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</ifModule>
The instructions and code above will work on Apache. If they are not working there is another way that may work for you. If the above code did not seem to work, remove it from your .htaccess file and try this one instead...
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
There is no mod_gzip for Apache 2.2x, as it is an Apache 1.3x module. Apache 2.2x has a only mod_deflate. GZip is the same DEFLATE plus a checksum and header/footer. As you can see here, Deflate is faster.


mod_deflate module is shipped with Apache, but in order to use it, you should enable one in your httpd.conf file (it is initially commented out):
LoadModule deflate_module modules/mod_deflate.so
After that just add following to the httpd.conf:
SetOutputFilter DEFLATE 
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/javascript
For more tuning refer, as was mentioned: http://httpd.apache.org/docs/2.2/mod/mod_deflate.html

HOW TO ENABLE GZIP COMPRESSION IN APACHE

The mod_deflate module provides the DEFLATE output filter that allows the output from your server to be compressed before being sent to the client over the network. mod_deflate is the replacement of mod_gzip which was used with an older version of Apache.

ENABLE GZIP MODULE IN APACHE

       By default, a mod_deflate module is enabled in Apache. To make sure that it is enabled, check following line in Apache configuration file.
LoadModule deflate_module modules/mod_deflate.so

CONFIGURE GZIP COMPRESSION

      Now edit your apache configuration and add the extensions you want to compress.
nano /etc/httpd/conf/httpd.conf
Add the following lines at end of the file
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
Restart Apache service,
## CentOS ##
sudo service httpd restart
## Ubuntu ##
sudo /etc/init.d/apache2 restart
Add the following configuration in Apache Virtual Host to enable gzip compression for your website. You can also add this code in website's .htaccess file in the site root.
<Directory /var/www/html/>
   <IfModule mod_mime.c>
 AddType application/x-javascript .js
 AddType text/css .css
   </IfModule>
   <IfModule mod_deflate.c>
 AddOutputFilterByType DEFLATE text/css application/x-javascript text/x-component text/html text/plain text/xml application/javascript
 <IfModule mod_setenvif.c>
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4.0[678] no-gzip
  BrowserMatch bMSIE !no-gzip !gzip-only-text/html
 </IfModule>
    </IfModule>
    Header append Vary User-Agent env=!dont-vary
</Directory>
TESTING COMPRESSION
We have enabled gzip compression, let’s use one of below online tools to verify gzip is working correctly.

Nodejs Gzip write after end error zlib

Source:

nce a writable stream is closed, it cannot accept anymore data (see the documentation): this is why on the first execution your code will work, and on the second you’ll have thewrite after enderror.
Just create a new gunzip stream for each request:
http.get(url, function(req) {
   var gunzip = zlib.createGzip();
   req.pipe(gunzip);

   gunzip.on('data', function (data) {
     decoder.decode(data);
   });

   gunzip.on('end', function() {
     decoder.result();
   });

});

anti-pattern là gì

  Trong công nghệ và lập trình, Anti-pattern (mẫu phản diện) là những giải pháp bề ngoài có vẻ hiệu quả để giải quyết một vấn đề phổ biến, ...