基于ECS快速部署WordPress的安全防护

本文的安全最佳实践以在阿里云服务器ECS上部署WordPress为例,详细介绍如何利用阿里云的云服务与安全措施来构建一个既安全又易于管理的WordPress环境。通过本文的安全防护措施,不仅能确保您的网站免受常见网络威胁的影响,还能享受到便捷高效的运维体验。

说明

本实践以使用Terraform在阿里云上快速部署WordPress为例,其他更多部署WordPress的方法,可参见搭建WordPress网站。Terraform是一个开源的自动化资源编排工具。资源编排服务ROS(Resource Orchestration Service)为Terraform提供了托管的能力,您可以创建Terraform类型的模板和资源栈,编排阿里云、AWSAzure资源。Terraform的详细介绍及使用方法,请参见Terraform

安全设计原则

  • 任何资源中不应保存长期有效的凭证信息,例如阿里云AccessKeyAccessSecret、数据库用户名密码等。

  • 定期自动轮转凭证信息,以便在意外事件或攻击导致的凭证信息泄露时可以实现漏洞止血。

  • 在确定时间周期内修复已知的OS安全漏洞,以符合安全补丁管理基线要求。

  • 定期备份关键数据,以确保即使发生了非预期的数据丢失也可以恢复数据。

部署架构

在云服务器ECS中部署WordPress使用的安全架构如下。

image

在部署WordPress过程中需要创建ECS实例和RDS MySQL数据库,本实践针对这两者做的安全配置如下:

  • ECS实例

    • 通过安全组精细控制ECS实例的出入流量

      安全组是一种虚拟防火墙,能够控制ECS实例的出入站流量。安全组的入方向规则控制ECS实例的入站流量,出方向规则控制ECS实例的出站流量。关于安全组使用的安全实践建议请参考安全组概述

    • 使用自动快照策略对云盘数据周期性备份

      使用自动快照策略对运行关键应用的云盘进行定期数据备份,应对因误删、勒索病毒等造成的数据丢失或应用系统故障,确保在需要时能快速恢复云盘数据以保证业务的连续性。更多信息,请参见自动快照

    • 通过RAM授权获取访问凭据

      实例RAM角色允许您将一个角色关联到ECS实例,在ECS实例内部基于STS(Security Token Service)临时凭证访问其他云产品的API,临时凭证将周期性更新。既可以保证云账号AccessKey的安全性(即无需将固定AccessKey明文放入ECS实例内部),也可以借助访问控制RAM实现精细化控制和权限管理。更多信息,请参见实例RAM角色

    • 使用KMS加密数据

      ECS可以使用密钥管理服务KMS提供的密钥为云盘加密,确保存储在云盘中的数据免受未经授权的访问和泄露,以保护数据的安全性。更多信息,请参见加密云盘

    • 定期获取访问RDS的凭据

      ECS通过云助手定时执行命令从而定期刷新并获取RDS数据库的凭据,以提高访问的安全性。

    • 使用补丁管理自动修复安全漏洞

      大多数企业对于IT资产(包括阿里云ECS实例)往往有一些合规要求,例如要求实例的系统漏洞尽快修复以避免受到安全攻击、需要所使用的一些软件包的版本始终保持到最新版本等。系统运维管理OOS的补丁管理可以使用安全相关更新及其他更新类型自动执行实例修补,您可以使用补丁管理来应用操作系统和应用程序的补丁。更多信息,请参见补丁管理概述

  • RDS MySQL数据库

    • 定期轮转RDS凭据

      数据库账号口令、服务器账号口令、SSH Key、访问密钥等凭据的泄露,是当今数据安全面临的主要威胁之一。为了降低数据泄露的安全风险,执行有效的凭据保护和定期轮转非常关键。针对阿里云关系型数据库RDS,KMS凭据管家(Secrets Manager)支持配置动态RDS凭据,对凭据进行全自动的定期轮换,降低业务数据面临的安全威胁。关于KMS凭据管家的更多说明请参见凭据管家概述

      说明

      凭据管家在以双账号模式轮转RDS凭据时,会分别使用ACSCurrentACSPrevious引用其中一个账号。凭据轮转时,ACSPrevious引用账号的口令会被重置为新的随机口令,随后凭据管家会再交换ACSCurrentACSPrevious账号引用以为下次密钥轮转作准备。凭据管家会默认返回ACSCurrent给应用程序,只要应用程序在轮转周期内(如15天内)访问过凭据管家,即可以获得轮转后的凭据。更多信息,请参见轮转通用凭据

    • 使用KMS加密数据

      RDS MySQL提供了免费的云盘加密功能,该功能基于块存储对整个数据盘进行加密,即使数据备份泄露也无法解密,能够保护您的数据安全。更多信息,请参见云盘加密

部署过程及安全措施

以下分步骤为您介绍在创建各资源过程中如何做好相关安全配置能力以及对应的Terraform模板示例。各部分Terraform配置文件之间存在依赖,无法单独运行,请您直接下载完整的Terraform配置文件(安全部署WordPress)并运行代码,完成ECS实例及其资源的自动创建、WordPress自动部署以及相关的安全配置。

步骤1:创建最小权限的安全组规则

本实践用于创建安全组及安全组规则使用的Terraform模板如下,该示例表示安全组仅放行入方向Linux实例的22端口用于SSH连接以及特定IP地址。您可以根据需要放行所需的端口号。例如,远程连接MySQL数据库时,需要放行MySQL默认占用的3306端口。

重要

该示例暂时关闭80端口,以避免非预期的部署阶段访问,待WordPress部署完成后,请您手动放行安全组规则的80端口。

# 创建VPC
resource "alicloud_vpc" "default" {
	cidr_block = "172.16.0.0/16"
	vpc_name = "${var.name}-vpc"
}

# 创建安全组
resource "alicloud_security_group" "default" {
	name = "${var.name}-sg"
	vpc_id = alicloud_vpc.default.id
	inner_access_policy = "Drop"
}

# 定义安全组入方向规则
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 = "<修改为允许访问的特定IP地址>"
	security_group_id = alicloud_security_group.default.id
}

// 默认关闭80端口以避免非预期的部署阶段访问
// 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
// }

步骤2:创建ECS实例

创建自动快照策略

本实践用于创建自动快照策略的Terraform模板如下,通过该模板会在每周三的23点、次日0点/1点/2点/3点自动创建快照,并将快照保存14天,您可以根据业务运行情况自行配置创建快照的开始时间。

#创建自动快照策略
resource "alicloud_ecs_auto_snapshot_policy" "default" {
	repeat_weekdays = ["3"]
	time_points = ["0", "1", "2", "3", "23"]
	retention_days = 14
}

创建RAM角色

本实践用于创建RAM角色的Terraform模板如下,通过该模板会创建名称为 wordpressRAM策略,并将其授予名称为 wordpressRAM角色,以允许ECS实例扮演该角色执行指定的KMS操作。

#定义RAM策略
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
}

#定义RAM角色
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
}

# 将RAM策略授予RAM角色
resource "alicloud_ram_role_policy_attachment" "wordpress" {
	policy_name = alicloud_ram_policy.wordpress.id
	policy_type = "Custom"
	role_name = alicloud_ram_role.wordpress.name
}

创建ECS实例,并配置云盘加密、RAM角色、自动快照备份策略

本实践用于创建ECS实例的Terraform模板如下,通过该模板可以实现:

  • 创建ECS实例,配置可用区、实例类型、镜像、安全组、交换机、实例名称、公网带宽计费方式等信息。

  • 为系统盘配置加密属性,确保存储在云盘中的数据都是加密状态。

  • 为系统盘关联已创建的自动快照策略,实现云盘数据定期备份。

  • 将已创建的RAM角色授予ECS实例,实现无AccessKey调用API,确保了云账号AccessKey的安全性。

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
}

步骤3:创建高可用RDS实例

创建RDS MySQL实例

本实践通过以下Terraform模板创建RDS MySQL实例,通过该模板可以创建名称为tf-wordpressRDS MySQL实例,并配置云盘加密能力。该数据库实例仅在VPC内部可访问,避免了对外暴露公网地址带来的安全隐患。

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]
    // encryption_key 可以被额外指定以使用其他密钥进行数据加密
    // 通过指定role_arn以使用默认CMK以用于RDS云盘加密
	role_arn                 = alicloud_ram_role.rds.arn
}

// 创建默认RDS RAM role以避免手动通过控制台授权
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
}

创建RDS数据库及数据库账号

本实践通过以下Terraform模板创建RDS数据库及数据库账号。

  • 创建名称为wordpress的数据库,数据库密码随机生成,有效提高数据库的安全性。

  • 分别创建了2个权限相同的数据库账号(wordpresswordpress_backup)。两个数据库账号可以用于凭据管家的密钥轮转,避免密钥轮转过程中的不可用问题,提升整体系统的稳定性。

#定义随机生成的密码
resource "random_string" db_passwd {
	length = 16
	special = false
}

#创建数据库
resource "alicloud_db_database" "default" {
  instance_id = alicloud_db_instance.tf-wordpress.id
  name        = "wordpress"
}

#创建第一个数据库账号,并设置其密码为之前生成的随机字符串
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
}

#授予该账号有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
}

#创建第二个数据库账号,并设置其密码为之前生成的随机字符串
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
}

#授予该账号有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
}

配置密钥管家能力以自动化轮转RDS凭据

本实践通过以下Terraform模板设置使用KMS凭据管家来管理RDS凭据,并启用双账号模式下的自动定期轮换功能,轮转周期为15天。

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
	})
}

步骤4:在ECS部署WordPress并配置密钥定期更新

部署WordPress应用

本实践通过以下Terraform模板部署WordPress应用,主要实现:

  • 安装PHP、httpd、aliyun-cli、jq等相关依赖。

  • 下载并安装WordPress相关软件包到/var/www/html/wordpress/目录。

  • 配置wp-config.php以使用环境变量文件并使用文件锁以安全并发获取数据库连接凭据。

  • 配置httpd禁止访问"."开头的文件,如.WORDPRESS_DB_PASSWORD_FILE,防止这些敏感信息被外部访问。

重要

此步骤结束后,请您放行安全组规则的80端口,以便访问WordPress,例如在浏览器输入http://<您的ECS公网IP>,进入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"
	}
}

定期刷新并获取RDS凭据

以下Terraform模板展示ECS如何定期刷新并获取RDS数据库的凭据,以提高访问的安全性。它主要通过以下步骤实现:

  1. 创建一个名为tf-secret-rotation的命令,该命令包含了用于从阿里云KMS服务获取最新密钥并更新到指定文件位置的脚本。

  2. 通过云助手每60分钟执行一次上述定义的命令,从而自动刷新RDS凭据。

该配置具备以下安全性:

  • 使用元数据服务提供的临时凭证代替长期有效的AccessKey,减少了因长期密钥泄露导致的安全风险。

  • 通过定期更换数据库凭据,即使有潜在的安全漏洞被利用,也能有效限制攻击者对数据库的访问时间窗口。

  • 结合适当配置的安全组规则,可以进一步增强整体系统的安全性,防止未授权访问或横向扩展攻击。

该模板搭配凭据管家会自动完成定期的RDS凭据轮转保证更高的安全性。如您需进行手动密钥轮转,则在调用了凭据管家的RotateSecret相关API后需手动调用此命令资源(在本例中为tf-secret-rotation)以完成完整的密钥轮转流程。

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)"
}

步骤5:配置补丁基线和自动漏洞修复

以下Terraform模板配置了默认安装的补丁策略:

  • 周期:每隔7天凌晨执行一次

  • 允许自动安装补丁,但不进行自动重启

  • 安装补丁前自动创建云盘快照并且保留7

data "alicloud_regions" "current_region_ds" {
	current = true
}

#创建RAM角色
resource "alicloud_ram_role" "oos" {
	name = "oos"
	document    = <<EOF
{
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "oos.aliyuncs.com"
        ]
      }
    }
  ],
  "Version": "1"
}
EOF
}

#创建RAM策略
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
}

#将RAM策略授予RAM角色
resource "alicloud_ram_role_policy_attachment" "oos" {
	policy_name = alicloud_ram_policy.oos.id
	policy_type = "Custom"
	role_name = alicloud_ram_role.oos.name
}

#执行OOS模板
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}",
	})
}

总结

安全防御从来不局限于单点产品安全能力,需要从数据安全、网络安全、身份与访问控制、操作系统安全、应用安全等多维度进行纵深防御。更多信息,请参见数据安全网络安全身份与访问控制等。