This topic describes how to use Alibaba Cloud services and security measures to deploy WordPress on an Elastic Compute Service (ECS) instance in a secure and easy-to-manage manner. The security measures in this topic protect your website from common network threats and provide you with a convenient and efficient O&M experience.
In this example, Terraform is used to deploy WordPress on an ECS instance. For more information about how to deploy WordPress, see Build a WordPress website. Terraform is an open source tool for automated resource orchestration. Terraform is available as a managed service in Resource Orchestration Service (ROS). You can create Terraform templates and stacks to orchestrate Alibaba Cloud, Amazon Web Services (AWS), and Microsoft Azure resources. For information about Terraform, see Terraform.
Security design principles
Do not store long-term valid credentials, such as Alibaba Cloud AccessKey IDs and AccessKey secrets, and database usernames and passwords, in resources.
Automatically rotate credentials on a regular basis to mitigate the impacts of credential leaks due to unexpected events or attacks.
Fix known operating system vulnerabilities within a specific period of time to meet baseline requirements for security patch management.
Back up critical data on a regular basis to ensure that data can be restored even if unexpected data loss occurs.
Architecture
The following figure shows the security architecture used to deploy WordPress on an ECS instance.
When you deploy WordPress, you must create an ECS instance and an ApsaraDB RDS for MySQL instance. In this example, you can configure the following security settings for the ECS instance and the ApsaraDB RDS for MySQL instance:
ECS instance
Add security group rules to the security groups to which the ECS instance belongs to manage inbound and outbound traffic of the instance.
A security group is a virtual firewall that controls inbound and outbound traffic of ECS instances. You can configure inbound rules for a security group to control traffic to ECS instances in the security group and outbound rules to control traffic from the instances. For information about how to use security groups, see Overview.
Use automatic snapshot policies to periodically back up disk data.
Use automatic snapshot policies to back up data on disks that run critical applications on a regular basis. This way, you can quickly restore disk data to ensure business continuity if data loss or application errors occur due to issues, such as accidental operations and ransom viruses. For more information, see Automatic snapshot policies.
Obtain access credentials by using Resource Access Management (RAM) based authorization.
You can attach an instance RAM role to an ECS instance. Then, the ECS instance can use the Security Token Service (STS) temporary credential of the instance RAM role to access the APIs of other Alibaba Cloud services. The STS temporary credential is updated on a periodic basis. This ensures the security of your AccessKey pair and implements fine-grained access control and permissions management by using RAM. You do not need to store the AccessKey pair in plaintext on the ECS instance. For more information, see Instance RAM roles.
Use Key Management Service (KMS) to encrypt data.
You can use KMS keys to encrypt disks attached to ECS instances. This ensures that data stored on disks is protected from unauthorized access and disclosure. For more information, see Cloud disk encryption.
Periodically obtain an ApsaraDB RDS secret to access the ApsaraDB RDS for MySQL instance.
To improve access security, ECS refreshes and obtains the ApsaraDB RDS secret of the ApsaraDB RDS database on a regular basis by running commands on Cloud Assistant.
Automatically fix security vulnerabilities by using the patch management feature.
Most enterprises have specific compliance requirements for IT assets, including Alibaba Cloud ECS instances. For example, the system vulnerabilities on the instances must be fixed at the earliest opportunity to prevent attacks and specific software packages must be kept up to date. The patch management feature of CloudOps Orchestration Service (OOS) can automatically install security patches and other software patches. The feature can manage patches in operating systems and applications. For more information, see Overview of Patch Manager.
ApsaraDB RDS for MySQL
Rotate the ApsaraDB RDS secret of the instance on a regular basis.
The leaks of secrets, such as passwords of database accounts, passwords of server accounts, SSH keys, and AccessKey pairs, are one of the main threats to data security. To mitigate the risks of data leaks, you must effectively protect and regularly rotate secrets. For ApsaraDB RDS instances, Secrets Manager allows you to configure dynamic ApsaraDB RDS secrets that are automatically rotated on a regular basis. This reduces the security threats to business data. For more information about Secrets Manager, see Overview of Secrets Manager.
NoteIf Secrets Manager manages ApsaraDB RDS secrets in dual-account mode, one account is referenced by the ACSCurrent version and the other account is referenced by the ACSPrevious version. When a secret is rotated, the password of the account referenced by the ACSPrevious version is reset to a new random password. Then, Secrets Manager switches the referenced accounts between the ACSCurrent and ACSPrevious versions for the next secret rotation. By default, ACSCurrent is returned to the application. If the application accesses Secrets Manager within the rotation period, such as within 15 days, the application can obtain the rotated secret. For more information, see Rotate generic secrets.
Use KMS to encrypt data.
The cloud disk encryption feature is free of charge for ApsaraDB RDS for MySQL. The feature encrypts the data on each disk of your ApsaraDB RDS for MySQL instance by using block storage to ensure data security. This way, your data cannot be decrypted even if the data is leaked. For more information, see Disk encryption.
Deployment process and security measures
The following steps describe how to configure security settings during resource creation by using sample Terraform templates. Terraform configuration files are dependent on each other and cannot be separately executed. You must download the complete Terraform configuration file (Securely Deploy WordPress) and run the code to automatically create ECS instances and related resources, deploy WordPress, and configure security settings.
Step 1: Create a security group rule with least privilege
The following sample Terraform template is used to create a security group and add rules to the security group. In this example, the security group allows only inbound SSH connections and specific IP addresses on port 22 of Linux instances. You can open ports based on your business requirements. For example, if you want to connect to a MySQL database on an ECS instance, add an inbound security group rule to a security group to which the instance belongs to open port 3306, which is the default port for MySQL.
In this example, port 80 is temporarily disabled to prevent unexpected access during WordPress deployment. After WordPress is deployed, you must manually open port 80 in the security group rule of the security group to which the instance belongs.
# Create a virtual private cloud (VPC).
resource "alicloud_vpc" "default" {
cidr_block = "172.16.0.0/16"
vpc_name = "${var.name}-vpc"
}
# Create a security group.
resource "alicloud_security_group" "default" {
name = "${var.name}-sg"
vpc_id = alicloud_vpc.default.id
inner_access_policy = "Drop"
}
# Add an inbound rule to the security group.
resource "alicloud_security_group_rule" "default" {
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
# port_range = "22/1024"
port_range = "22/22"
cidr_ip = "<Specific IP address that is allowed for access>"
security_group_id = alicloud_security_group.default.id
}
// Close port 80 to prevent unexpected access during WordPress deployment.
// resource "alicloud_security_group_rule" "web" {
// type = "ingress"
// ip_protocol = "tcp"
// nic_type = "intranet"
// policy = "accept"
// # port_range = "22/1024"
// port_range = "80/80"
// cidr_ip = "A.B.C.D/0"
// security_group_id = alicloud_security_group.default.id
// }Step 2: Create an ECS instance
Create an automatic snapshot policy
The following sample Terraform template is used to create an automatic snapshot policy. This template automatically creates snapshots at 23:00 every Wednesday and at 00:00, 01:00, 02:00, and 03:00 every Thursday. The snapshots are retained for 14 days. You can specify the start time of snapshot creation based on your business requirements.
# Create an automatic snapshot policy.
resource "alicloud_ecs_auto_snapshot_policy" "default" {
repeat_weekdays = ["3"]
time_points = ["0", "1", "2", "3", "23"]
retention_days = 14
}Create an instance RAM role
The following sample Terraform template is used to create a RAM role. The template creates a RAM policy named wordpress and attaches the RAM policy to the RAM role named wordpress to allow the ECS instance to assume the role to perform specified KMS operations.
# Configure a RAM policy.
resource "alicloud_ram_policy" "wordpress" {
policy_name = "wordpress"
policy_document = <<EOT
{
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:GetSecretValue"
],
"Resource": "${alicloud_kms_secret.wordpress_db_passwd.arn}"
},
{
"Effect": "Allow",
"Action": [
"kms:DescribeSecret",
"kms:ListSecretVersionIds"
],
"Resource": "*"
}
],
"Version": "1"
}
EOT
}
# Create a RAM role.
resource "alicloud_ram_role" "wordpress" {
name = "wordpress"
document = <<EOF
{
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": [
"apigateway.aliyuncs.com",
"ecs.aliyuncs.com"
]
}
}
],
"Version": "1"
}
EOF
}
# Attach the RAM policy to the RAM role.
resource "alicloud_ram_role_policy_attachment" "wordpress" {
policy_name = alicloud_ram_policy.wordpress.id
policy_type = "Custom"
role_name = alicloud_ram_role.wordpress.name
}Create an ECS instance, configure disk encryption, attach the RAM role to the instance, and associate the automatic snapshot policy to the system disk
The following sample Terraform template is used to perform the following operations:
Create an ECS instance and configure the zone, instance type, image, security group, vSwitch, instance name, and billing method for network usage.
Enable encryption for the system disk to ensure that the data stored on the system disk is encrypted.
Associate an automatic snapshot policy with the system disk to back up disk data on a regular basis.
Configure the ECS instance to assume the created RAM role to call API operations without the need to enter an AccessKey pair. This ensures the security of the Alibaba Cloud account for which the AccessKey pair is configured.
resource "alicloud_instance" "default" {
availability_zone = data.alicloud_zones.default.zones.0.id
instance_type = data.alicloud_instance_types.default.instance_types.0.id
image_id = data.alicloud_images.default.images.0.id
security_groups = [alicloud_security_group.default.id]
vswitch_id = alicloud_vswitch.default.id
instance_name = "${var.name}-instance"
internet_charge_type = "PayByTraffic"
internet_max_bandwidth_out = 5
system_disk_category = "cloud_essd"
// key_name = "YOUR SSH PUBKEY NAME"
role_name = alicloud_ram_role.wordpress.name
system_disk_auto_snapshot_policy_id = "${alicloud_ecs_auto_snapshot_policy.default.id}"
system_disk_encrypted = true
}Step 3: Create a high-availability ApsaraDB RDS instance
Create an ApsaraDB RDS for MySQL instance
The following sample Terraform template is used to create an ApsaraDB RDS for MySQL instance named tf-wordpress and enable encryption for cloud disks attached to the instance. The instance can be accessed only in the VPC. This prevents security risks caused by public endpoint exposure.
variable "db_engine" {
default = "MySQL"
}
variable "db_engine_version" {
default = "8.0"
}
variable "db_charge_type" {
default = "PostPaid"
}
variable "db_category" {
default = "HighAvailability"
}
variable "db_storage_type" {
default = "cloud_essd"
}
data "alicloud_db_instance_classes" "tf" {
zone_id = data.alicloud_zones.default.zones.0.id
engine = "${var.db_engine}"
engine_version = "${var.db_engine_version}"
category = "${var.db_category}"
db_instance_storage_type = "${var.db_storage_type}"
instance_charge_type = "${var.db_charge_type}"
}
resource "alicloud_db_instance" "tf-wordpress" {
engine = "${var.db_engine}"
engine_version = "${var.db_engine_version}"
instance_type = data.alicloud_db_instance_classes.tf.instance_classes.0.instance_class
instance_storage = data.alicloud_db_instance_classes.tf.instance_classes.0.storage_range.min
instance_charge_type = "Postpaid"
instance_name = "tf-wordpress"
vswitch_id = alicloud_vswitch.default.id
monitoring_period = "60"
db_instance_storage_type = "${var.db_storage_type}"
security_group_ids = [alicloud_security_group.db.id]
// The encryption_key can be additionally specified to encrypt data by using other keys.
// Specify role_arn to use the default customer master key (CMK) for RDS disk encryption.
role_arn = alicloud_ram_role.rds.arn
}
// Create a default RDS RAM role to eliminate the need for manual authorization in the console.
resource "alicloud_ram_role" "rds" {
name = "AliyunRDSInstanceEncryptionDefaultRole"
document = <<EOF
{
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": [
"rds.aliyuncs.com"
]
}
}
],
"Version": "1"
}
EOF
}
resource "alicloud_ram_role_policy_attachment" "rds" {
policy_name = "AliyunRDSInstanceEncryptionRolePolicy"
policy_type = "System"
role_name = alicloud_ram_role.rds.name
}Create a database on the ApsaraDB RDS for MySQL instance and create database accounts
The following sample Terraform template is used to create a database on the ApsaraDB RDS for MySQL instance and create database accounts.
Create a database named
wordpress. A random database password is generated to improve database security.Create two database accounts named
wordpressandwordpress_backupto which the same permissions are granted. The two database accounts can be used by Secrets Manager to rotate the ApsaraDB RDS secret. This prevents unavailability during key rotation and improves system stability.
# Configure settings for a random password.
resource "random_string" db_passwd {
length = 16
special = false
}
# Create a database.
resource "alicloud_db_database" "default" {
instance_id = alicloud_db_instance.tf-wordpress.id
name = "wordpress"
}
# Create the first database account and set the password to the preceding random password.
resource "alicloud_rds_account" "default" {
db_instance_id = alicloud_db_instance.tf-wordpress.id
account_name = "wordpress"
account_password = resource.random_string.db_passwd.result
}
# Grant the account the read and write permissions on the database named wordpress.
resource "alicloud_db_account_privilege" "privilege" {
instance_id = alicloud_db_instance.tf-wordpress.id
account_name = alicloud_rds_account.default.account_name
privilege = "ReadWrite"
db_names = alicloud_db_database.default.*.name
}
# Create the second database account and set the password to the preceding random password.
resource "alicloud_rds_account" "backup" {
db_instance_id = alicloud_db_instance.tf-wordpress.id
account_name = "wordpress_backup"
account_password = resource.random_string.db_passwd.result
}
# Grant the account the read and write permissions on the database named wordpress.
resource "alicloud_db_account_privilege" "privilege-backup" {
instance_id = alicloud_db_instance.tf-wordpress.id
account_name = alicloud_rds_account.backup.account_name
privilege = "ReadWrite"
db_names = alicloud_db_database.default.*.name
}Configure Secrets Manager to automatically rotate the ApsaraDB RDS secret of the ApsaraDB RDS for MySQL instance
The following sample Terraform template is used to use KMS Secrets Manager to manage the ApsaraDB RDS secret of the ApsaraDB RDS for MySQL instance and enable the automatic periodic rotation feature in dual-account mode. The rotation period is 15 days.
resource "alicloud_kms_secret" "wordpress_db_passwd" {
secret_name = "wordpress_db_passwd"
description = "from terraform"
secret_data = jsonencode({
Accounts = [
{
AccountName = alicloud_rds_account.default.account_name
AccountPassword = resource.random_string.db_passwd.result
},
{
AccountName = alicloud_rds_account.backup.account_name
AccountPassword = resource.random_string.db_passwd.result
}
]
})
version_id = "000000000001"
force_delete_without_recovery = true
rotation_interval = "15d"
secret_type = "Rds"
enable_automatic_rotation = true
extended_config = jsonencode({
"SecretSubType" = "DoubleUsers",
"DBInstanceId" = alicloud_db_instance.tf-wordpress.id
})
}Step 4: Deploy WordPress on the ECS instance and configure regular key update
Deploy WordPress
The following sample Terraform template is used to deploy WordPress. The following operations are performed:
Install dependencies, such as PHP, httpd, aliyun-cli, and jq.
Download the WordPress-related software package to the
/var/www/html/wordpress/directory and install WordPress.Configure wp-config.php to use an environment variable file and file locks to obtain database connection credentials for secure concurrent connections.
Configure httpd to prohibit access to files whose file names start with a period (.), such as
.WORDPRESS_DB_PASSWORD_FILE, to prevent sensitive information from being accessed.
After this step is complete, open port 80 in the security group rule of the security group to allow access to WordPress. For example, enter http://<Public IP address of your ECS instance> in a browser to go to the WordPress installation page. Then, follow the on-screen instructions to install WordPress.
resource "alicloud_ecs_command" "default" {
name = "tf-command"
command_content = base64encode(<<EOT
set -e
echo hello world > /tmp/hello.txt
wget https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz
tar xzvf aliyun-cli-linux-latest-amd64.tgz
mv aliyun /usr/local/bin/aliyun
rm -rf aliyun-cli-linux-latest-amd64.tgz
dnf install -y httpd
dnf install -y httpd-tools php php-cli php-json php-gd php-mbstring php-pdo php-xml php-mysqlnd php-pecl-zip wget
dnf install -y jq
cd /var/www/html
wget https://wordpress.org/latest.tar.gz
tar xvzf latest.tar.gz
rm -rf latest.tar.gz
cat > /etc/httpd/conf.d/wordpress.conf << EOF
<VirtualHost *:80>
ServerAdmin root@localhost
DocumentRoot /var/www/html/wordpress
<Directory "/var/www/html/wordpress">
Options Indexes FollowSymLinks
AllowOverride all
Require all granted
</Directory>
ErrorLog /var/log/httpd/wordpress_error.log
CustomLog /var/log/httpd/wordpress_access.log common
</VirtualHost>
EOF
cat > /var/www/html/wordpress/.WORDPRESS_DB_USER_FILE << "EOF"
wordpress
EOF
cat > /var/www/html/wordpress/.WORDPRESS_DB_PASSWORD_FILE << "EOF"
${resource.random_string.db_passwd.result}
EOF
cat > /var/www/html/wordpress/.WORDPRESS_DB_HOST_FILE << "EOF"
${alicloud_db_instance.tf-wordpress.connection_string}
EOF
# disable any access to the hidden files
cat > /var/www/html/wordpress/.htaccess << "EOF"
<FilesMatch "^\.">
Order allow,deny
Deny from all
</FilesMatch>
EOF
for KEY in AUTH_KEY SECURE_AUTH_KEY LOGGED_IN_KEY NONCE_KEY AUTH_SALT SECURE_AUTH_SALT LOGGED_IN_SALT NONCE_SALT; do
cat > /var/www/html/wordpress/.WORDPRESS_$${KEY}_FILE << EOF
$(openssl rand -base64 48)
EOF
done
touch /var/www/html/wordpress/.env.lock
cat > /var/www/html/wordpress/wp-config.php << "EOF"
<?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the installation.
* You don't have to use the web site, you can copy this file to "wp-config.php"
* and fill in the values.
*
* This file contains the following configurations:
*
* * Database settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* This has been slightly modified (to read environment variables) for use in Docker.
*
* @link https://wordpress.org/documentation/article/editing-wp-config-php/
*
* @package WordPress
*/
// IMPORTANT: this file needs to stay in-sync with https://github.com/WordPress/WordPress/blob/master/wp-config-sample.php
// (it gets parsed by the upstream wizard in https://github.com/WordPress/WordPress/blob/f27cb65e1ef25d11b535695a660e7282b98eb742/wp-admin/setup-config.php#L356-L392)
// a helper function to lookup "env_FILE", "env", then fallback
if (!function_exists('getenv_docker')) {
// https://github.com/docker-library/wordpress/issues/588 (WP-CLI will load this file 2x)
function getenv_docker($env, $default) {
$fileName = __DIR__ . '/.' . $env . '_FILE';
if (file_exists($fileName)) {
$lockfile = __DIR__ . '/' . '.env.lock';
$fp = fopen($lockfile, 'r+');
if (flock($fp, LOCK_SH)) {
$env = rtrim(file_get_contents($fileName), "\r\n");
flock($fp, LOCK_UN);
}
return $env;
}
if (($val = getenv($env)) !== false) {
return $val;
}
else {
return $default;
}
}
}
// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', getenv_docker('WORDPRESS_DB_NAME', 'wordpress') );
/** Database username */
define( 'DB_USER', getenv_docker('WORDPRESS_DB_USER', 'example username') );
/** Database password */
define( 'DB_PASSWORD', getenv_docker('WORDPRESS_DB_PASSWORD', 'example password') );
/**
* Docker image fallback values above are sourced from the official WordPress installation wizard:
* https://github.com/WordPress/WordPress/blob/1356f6537220ffdc32b9dad2a6cdbe2d010b7a88/wp-admin/setup-config.php#L224-L238
* (However, using "example username" and "example password" in your database is strongly discouraged. Please use strong, random credentials!)
*/
/** Database hostname */
define( 'DB_HOST', getenv_docker('WORDPRESS_DB_HOST', 'mysql') );
/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', getenv_docker('WORDPRESS_DB_CHARSET', 'utf8') );
/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', getenv_docker('WORDPRESS_DB_COLLATE', '') );
/**#@+
* Authentication unique keys and salts.
*
* Change these to different unique phrases! You can generate these using
* the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}.
*
* You can change these at any point in time to invalidate all existing cookies.
* This will force all users to have to log in again.
*
* @since 2.6.0
*/
define( 'AUTH_KEY', getenv_docker('WORDPRESS_AUTH_KEY', 'put your unique phrase here') );
define( 'SECURE_AUTH_KEY', getenv_docker('WORDPRESS_SECURE_AUTH_KEY', 'put your unique phrase here') );
define( 'LOGGED_IN_KEY', getenv_docker('WORDPRESS_LOGGED_IN_KEY', 'put your unique phrase here') );
define( 'NONCE_KEY', getenv_docker('WORDPRESS_NONCE_KEY', 'put your unique phrase here') );
define( 'AUTH_SALT', getenv_docker('WORDPRESS_AUTH_SALT', 'put your unique phrase here') );
define( 'SECURE_AUTH_SALT', getenv_docker('WORDPRESS_SECURE_AUTH_SALT', 'put your unique phrase here') );
define( 'LOGGED_IN_SALT', getenv_docker('WORDPRESS_LOGGED_IN_SALT', 'put your unique phrase here') );
define( 'NONCE_SALT', getenv_docker('WORDPRESS_NONCE_SALT', 'put your unique phrase here') );
// (See also https://wordpress.stackexchange.com/a/152905/199287)
/**#@-*/
/**
* WordPress database table prefix.
*
* You can have multiple installations in one database if you give each
* a unique prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = getenv_docker('WORDPRESS_TABLE_PREFIX', 'wp_');
/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is strongly recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*
* For information on other constants that can be used for debugging,
* visit the documentation.
*
* @link https://wordpress.org/documentation/article/debugging-in-wordpress/
*/
define( 'WP_DEBUG', !!getenv_docker('WORDPRESS_DEBUG', '') );
/* Add any custom values between this line and the "stop editing" line. */
// define('WP_HOME','https://sampledomain.com/');
// define('WP_SITEURL','https://sampledomain.com/');
/** SSL */
// define('FORCE_SSL_ADMIN', true);
// If we're behind a proxy server and using HTTPS, we need to alert WordPress of that fact
// see also https://wordpress.org/support/article/administration-over-ssl/#using-a-reverse-proxy
// if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
// $_SERVER['HTTPS'] = 'on';
// }
// (we include this by default because reverse proxying is extremely common in container environments)
// Enable SSL/TLS between Wordpress and MYSQL database
// Define('MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL);//This activates SSL mode
// Define('MYSQL_SSL_CA', '/usr/src/wordpress/CERTNAME.pem');
/* That's all, stop editing! Happy publishing. */
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
EOF
chown -Rf apache:apache ./wordpress/
chmod -Rf 775 ./wordpress/
systemctl enable --now httpd
systemctl restart httpd
EOT
)
type = "RunShellScript"
timeout = 60 * 10
}
resource "alicloud_ecs_invocation" "default" {
instance_id = [alicloud_instance.default.id]
command_id = alicloud_ecs_command.default.id
timeouts {
create = "30m"
}
}Refresh and obtain the ApsaraDB RDS secret of the ApsaraDB RDS for MySQL instance on a regular basis
The following sample Terraform template allows the ECS instance to periodically refresh and obtain the ApsaraDB RDS secret of the ApsaraDB RDS for MySQL instance on a regular basis to improve access security.
Create a command named
tf-secret-rotationthat contains the script used to obtain the latest key file from Alibaba Cloud KMS and update the key file to the script stored in the specified directory.Use Cloud Assistant to run the preceding command every 60 minutes to refresh the ApsaraDB RDS secret.
The configurations provide the following security benefits:
The temporary credentials provided by the metadata service are used instead of a long-term valid AccessKey pair. This prevents security risks caused by long-term key leaks.
When you update ApsaraDB RDS secrets on a regular basis, you can limit the time window for attackers to access databases even if vulnerabilities are exploited.
You can update credentials on a regular basis together with correctly configured security group rules. This can improve the overall security of the system to prevent unauthorized access or scale-out attacks.
For higher security, the template is used together with Secrets Manager to automatically rotate the ApsaraDB RDS secret of the ApsaraDB RDS for MySQL instance on a regular basis. If you want to perform manual secret rotation, you must run a command, such as tf-secret-rotation, after you call the RotateSecret operation of Secrets Manager.
resource "alicloud_ecs_command" "secret_rotation" {
name = "tf-secret-rotation"
command_content = base64encode(<<EOT
#!/bin/bash
cat > /tmp/secret_rotation.sh << "EOF"
#!/bin/bash
echo "$(date --rfc-3339=seconds): Updating the secret" >> /var/log/secret_rotation.log
token=$(curl -X PUT "http://100.100.100.200/latest/api/token" -H "X-aliyun-ecs-metadata-token-ttl-seconds:60")
region_id=$(curl -H "X-aliyun-ecs-metadata-token: $token" http://100.100.100.200/latest/meta-data/region-id)
aliyun --mode EcsRamRole --ram-role-name wordpress --region $region_id kms GetSecretValue --SecretName wordpress_db_passwd |
jq '.SecretData|fromjson.AccountName' |tr -d '"' > /var/www/html/wordpress/.WORDPRESS_DB_USER_FILE
aliyun --mode EcsRamRole --ram-role-name wordpress --region $region_id kms GetSecretValue --SecretName wordpress_db_passwd |
jq '.SecretData|fromjson|.AccountPassword'|tr -d '"' > /var/www/html/wordpress/.WORDPRESS_DB_PASSWORD_FILE
EOF
flock /var/www/html/wordpress/.env.lock -c "/bin/bash /tmp/secret_rotation.sh"
EOT
)
type = "RunShellScript"
timeout = 30
}
resource "alicloud_ecs_invocation" "secret_rotation" {
instance_id = [alicloud_instance.default.id]
command_id = alicloud_ecs_command.secret_rotation.id
timeouts {
create = "30s"
}
repeat_mode = "Period"
frequency = "rate(60m)"
}Step 5: Configure patch baselines and automatic vulnerability fixing
The following sample Terraform template is used to execute the following default policy for installing patches:
Install patches every seven days in the early morning.
Enable automatic patch installation without the need for automatic instance restart.
Automatically create disk snapshots and retain them for seven days before patches are installed.
data "alicloud_regions" "current_region_ds" {
current = true
}
# Create a RAM role.
resource "alicloud_ram_role" "oos" {
name = "oos"
document = <<EOF
{
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": [
"oos.aliyuncs.com"
]
}
}
],
"Version": "1"
}
EOF
}
# Create a RAM policy.
resource "alicloud_ram_policy" "oos" {
policy_name = "oos"
policy_document = <<EOT
{
"Version": "1",
"Statement": [
{
"Action": [
"ecs:CreateSnapshot",
"ecs:DescribeCloudAssistantStatus",
"ecs:DescribeDisks",
"ecs:DescribeInstances",
"ecs:DescribeInvocationResults",
"ecs:DescribeInvocations",
"ecs:DescribeManagedInstances",
"ecs:DescribeSnapshots",
"ecs:RebootInstance",
"ecs:RunCommand"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"ecd:CreateSnapshot",
"ecd:DescribeCloudAssistantStatus",
"ecd:DescribeDesktops",
"ecd:DescribeInvocations",
"ecd:DescribeSnapshots",
"ecd:RebootDesktops",
"ecd:RunCommand"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"oos:ListInstancePatchStates",
"oos:StartExecution"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
EOT
}
# Attach the RAM policy to the RAM role.
resource "alicloud_ram_role_policy_attachment" "oos" {
policy_name = alicloud_ram_policy.oos.id
policy_type = "Custom"
role_name = alicloud_ram_role.oos.name
}
# Execute the OOS template.
resource "alicloud_oos_execution" "patch" {
template_name = "ACS-ECS-ScheduleApplyPatchBaseline"
description = "Auto patch software to satify the baseline"
parameters = jsonencode({
regionId: "${data.alicloud_regions.current_region_ds.regions.0.id}",
resourceType: "ALIYUN::ECS::Instance",
targets: {
ResourceIds: ["${alicloud_instance.default.id}"],
RegionId: "${data.alicloud_regions.current_region_ds.regions.0.id}",
Type: "ResourceIds",
},
timerTrigger: {
expression: "0 0 1 */7 * *",
type: "cron",
timeZone: "Asia/Shanghai",
endDate: "2099-04-04T04:00:00Z",
},
action: "install",
whetherCreateSnapshot: true,
retentionDays: 7,
rebootIfNeed: false,
OOSAssumeRole: "${alicloud_ram_role.oos.name}",
})
}Summary
Security defense is more than single-service security capabilities and requires in-depth defense from multiple dimensions, such as data security, network security, identity and access control, operating system security, and application security. For more information, see Data security, Network security, and Identity and access control.