Deploying a Web App in AWS China
Tags: aws, nginx, devops
Published: 2021-12-22 15:06:46

Authors: @aldorea & @sergiocb95 

The Chinese market is becoming more and more important for companies due to the volume of customers this market represents. Therefore, companies with digital products want to focus their products on the Chinese population. Companies using AWS services have the opportunity to do so, but with a number of limitations to consider when planning to move your services to China.

## Things to consider when using AWS services in China

### Signing up for AWS China Regions 

There are currently two AWS regions in China:Beijing and Ningxia. Global AWS accounts are not suitable for accessing services in China and vice versa. You have to create a separate account to access AWS services in China. But first we have to prove our identity to be able to use AWS services in China. For this we have to provide our business license issued by the Bureau of Industry and Commerce of People’s Republic of China (PRC) or a valid government agency. To do this, Amazon offers us the AWS China Gateway Portal which will guide us through the process of creating an account in this region.

### ICP Filing for Internet Information Services in Mainland China

In accordance with Chinese regulations. We have to fill in the Internet Content Provider (ICP) permit and there are two types. The “ICP Recordal” is the process for hosting a website providing non-commercial internet information services and “ICP License’” is to host a website providing commercial internet information services.

### Network Connections for AWS China Regions 

The AWS China and AWS global regions are not directly connected. To overcome packet loss and latency between the Chinese and global AWS regions, AWS works with local internet service providers such as China Mobile. The DirectConnect service is used to establish this solution by signing a contract with the provider as well as following Chinese regulations regarding the transfer and localization of information.

### Relevant Compliance and Regulatory Requirements in Mainland China

It is important that if we plan to move our services to China, we have to be aware of the legislation. AWS can facilitate this process by contacting their helpdesk. 

Apart from the ICP and depending on the type of business, the following regulations must be taken into account:

- Security obligations under China Cybersecurity Law, 
- Multi-level Protection Scheme (MLPS) Certification, 
- Cryptographic related regulations. 
 
## Our particular use case

In our particular use case, we had to deploy a fairly simple web page where users would have to fill in a series of forms to enter a competition. Their personal data would be stored in a database for further processing. As a fundamental requirement, the application had to be highly scalable, as we expected to receive high traffic peaks at certain times after running promotional campaigns on social media. These projects usually have very short development times (between 1 month and 2 weeks) so the developments tend to be quite hectic and results-oriented rather than maintainable.

All the projects we do for this client have very similar requirements, so we always use a very similar architecture and technology stack to optimize the development process:

- Front end app using React and NextJS framework.
- API REST using NodeJS and Express. It connects our front end app and our database.
- PostgreSQL as a database to store user data.

We deployed these projects on a Kubernetes cluster using the AWS EKS service. We use the same template to create the necessary resources in the cluster, so the deployment and configuration of a new project is quite straightforward.

However, in the case of this application we encounter a big problem: by Chinese regulations, no personal data of a Chinese citizen can leave China.

Neither our Kubernetes cluster nor any of our resources are located in China (but in the Frankfurt region). Therefore, due to Chinese regulatory restrictions it was not going to be possible to reuse the infrastructure we already had in place for this project.

Fortunately, AWS has a couple of special regions within China: Ningxia and Beijing. These regions, which make up AWS China, are segregated from the rest of the regions (AWS Global), have a much more limited number of services than the other regions and their interconnectivity with the rest of the AWS regions is practically non-existent.

We decided to simplify our infrastructure as much as possible. Instead of using EKS, we decided to use Elastic Beanstalk, an AWS service that we had not used so far but that seemed to fit perfectly with our use case. Elastic Beanstalk allows you to deploy scalable web applications in a very simple way, taking care of creating all the necessary AWS resources for its proper implementation (ec2, auto-scaling group, load balancer, etc). Therefore, it made deployment and configuration very easy for us. 

Once we managed to get it up and running (setting up infrastructure, continuous integration, etc.), we ran into another problem related to the Chinese regulations.

As discussed in the previous section, any website needs to have an ICP license. This license, among other things, links our website to an IP. Our client, right from the start of the project, provided us with an Elastic IP (fixed IP) for which he had already requested and obtained this license. 

At this point we realized one basic thing: how are we going to build a highly scalable application only accessible with a single Elastic IP? If we want the application to be highly scalable, we will have to be able to scale the application horizontally (i.e. increase the number of servers that host the application). However, the Elastic IP can only be associated to a single EC2 machine, which does not work for us. No problem, let's just use a very large server. The big problem with this is that it is not elastic at all: the capacity does not increase/decrease depending on the processing load at any given time, which leads to higher unnecessary costs.

Well, Beanstalk can provide us with a load balancer (ELB), allowing us to have several servers hosting the application but only one entry point. Why not associate the IP with the ELB? It turns out, however, that assigning an Elastic IP to an ELB is not possible. Internally, an ELB uses a pool of IPs that allows it to elastically scale its processing capacity, so assigning a single IP to it is not possible.

Therefore, we had to improvise an alternative solution, which would not be optimal but could help us to solve the problem.

## Rapid Solution. Configuring an EC2 instance as a reverse proxy 

If you are in a hurry to move your services in China as a temporary solution you can set up an EC2 instance as a reverse proxy. The first thing to do is to create an EC2 instance from the AWS China console which you have to create beforehand. Here is a link that shows you how to do it. Remember to configure the security groups to be able to access the instance via SSH and to be able to access port 80 and 443 from anywhere. We have installed certbot to get the SSL certificates for free with Let's Encrypt.

Once you have created the EC2 instance, in our case we selected Amazon Linux 2, and you can access it via SSH you have to install the Nginx web server and certbot. Let's do it !

First we need to update the OS (Amazon Linux 2) and install the Nginx web server. 

```bash
sudo yum update
sudo amazon-linux-extras install -y nginx1
sudo yum install certbot python2-certbot-nginx
```

Certbot is able to automatically configure SSL for Nginx but needs to find the `server_name` directive that matches the domain you are requesting the certificate for. This directive is found inside the server block. Let's modify the `nginx.conf` file.

```bash
sudo vi /etc/nginx/nginx.conf
server_name example.com www.example.com;
```

We save our changes and verify that we have respected the nginx syntax.

```bash
sudo nginx -t
```

If no error has occurred, we must restart our server for the changes to take effect.

```bash
sudo systemctl reload nginx
```

Now we are able to automatically configure our server to use **HTTPs**. Now we have to run the following command to make Certbot update our configuration file.

```bash
sudo certbot --nginx -d example.com -d www.example.com
```

Then Certbot will communicate with the Let's Encrypt server and check that you control the domain for which you are requesting the certificate. If everything is successful, Certbot will give you two options for configuring the https settings.

- Easy - Allow both HTTP and HTTPS access to these sites
- Secure - Make all requests redirect to secure HTTPS access

Once you have chosen one of these options Certbot will modify the Nginx configuration and restart it. It will also give you a message saying that the process was successful and where the certificates are stored.

Now you just need to make sure that the certificates are renewed automatically as let's encrypt certificates expire after 3 months. To do this we are going to configure a cron job with the Certbot command that allows us to renew the certificates.

```bash
sudo crontab -e
```

Our editor will open the default cron task tab and we will write the following task.

```bash
. . .
30 5 * * * /usr/bin/certbot renew --quiet
```

This task will be executed at 5:30 am everyday. The `renew` command for Cerbot will check if installed certificates are set to expire in less than one month and update them. And the `-quiet` option tells Certbot not to wait for user acceptance on certificate renewal.

Let's create the configuration file for our Nginx to act as a reverse proxy. Now we are going to create a file in the following path `/etc/nginx/default.d/proxy.conf`.

The `proxy_pass` directive indicates where the traffic has to be redirected, in our case it will be the url of our load balancer.

Once this is done we will load our main configuration file. The file will look like this:

```bash
location / {
 proxy_pass http://your-load-balancer-url;
 proxy_pass_header Server;
 proxy_hide_header X-Powered-By;
}
```

Then we restart our server and check that the syntax is correct (**as we have done in the previous steps**) and we would have our Nginx server ready with https traffic enabled and acting as a reverse proxy.

## The stable solution

Once the problem was solved in time and the day of the launch of the application arrived, we had the feeling that there should be a better way to solve this problem. We thought the situation was too common. It should have happened to someone before.

After searching and searching in Google, we found a document from SINNET, the company that manages the Beijing region in AWS China ([link](https://s3.cn-north-1.amazonaws.com.cn/sinnetcloud/%E5%85%AC%E5%AE%89%E5%A4%87%E6%A1%88/Description+of+ICP+Recordal.pdf)). This document redirected to another document with information on how to manage ICP licenses when using ELBs ([link](https://s3.cn-north-1.amazonaws.com.cn/sinnetcloud/%E5%85%AC%E5%AE%89%E5%A4%87%E6%A1%88/%E8%8E%B7%E5%8F%96ELB%E7%9A%84IP/ELB+preserved+IP+operation+guide+for+AWS+ICP+Recordal_V2.1(updated).pdf)).

Summarizing the content of the document, it seems that, when applying for an ICP license, it is possible to request several IPs, not just one. On the other hand, there is the possibility to request AWS China to only assign IPs to an ELB from a fixed set of IPs (the ones requested in the ICP license).

However, starting to use this approach at this stage is no longer possible. A new ICP request can take between 1 and 3 weeks to be accepted, so the current system will have to remain in place for at least a month.

We decided to write this article mainly to save the lives of those who, in the future, will encounter this problem and will be able to solve it in time. But we would also like to make some conclusions from this:

- Rushing is never good, leading to improvising in case of any problem.
- The client should tell you what he wants to do, not how, especially if he lacks technical knowledge of the domain. I'm not saying that they cannot contribute to it, but not to impose it without a previous discussion.