Как ограничить доступ к бакету Object Storage диапазоном IP-адресов, принадлежащих Cloud CDN
Описание задачи
Необходимо, чтобы контент статического сайта или SPA, размещенного в Yandex Object Storage, был доступен только для Yandex Cloud CDN.
Решение
Логика размещения статических сайтов в Object Storage предусматривает, что содержимое бакета должно быть доступно для всех IP-адресов и подсетей, которые запрашивают контент с сайта.
Инфраструктура Cloud CDN кеширует содержимое бакета с сайтом на своих серверах, однако в консоли управления пока отсутствует возможность настроить доступ к бакету исключительно из сетей CDN.
Для реализации этого сценария необходимо ограничить публичный доступ к бакету по IP-адресам, вручную разрешив подключения только из подсетей Yandex Cloud CDN, запрещая доступ к содержимому бакета для всех остальных IP-адресов. В этом случае необходимо настроить политику доступа для бакета, в которой будут указаны диапазоны IP-адресов, которые используются сервисом Cloud CDN.
Список подсетей провайдера Yandex Cloud CDN доступен в документации.
Список задействованных сервисом IP-адресов и подсетей расположен по ссылкам:
Примечание
Список адресов периодечски меняется: в него могут быть включены новые или удалены более незадействованные IP-адреса или подсети.
Рекомендуем время от времени сверять список подсетей в созданной вами политике доступа для бакета с актуальным списком адресов.
Примените политику из указанного ниже примера по этой инструкции.
Перед применением политики на основе приложенного примера следует изменить <имя бакета> на наименование вашего бакета в Object Storage.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "188.72.103.0/24"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "188.72.104.0/24"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "188.72.105.0/24"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "188.72.110.0/24"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "188.72.111.0/24"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "188.72.112.0/24"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "188.72.113.0/24"
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<имя бакета>/*",
"Condition": {
"IpAddress": {
"aws:sourceip": "89.223.9.0/24"
}
}
}
]
}
Список IP-адресов для CDN-провайдера EdgeCenter предоставлен по ссылке
В этом руководстве предоставлен пример скрипта на Python. Сохраните скрипт в файл acl_generator.py и запустите его с помощью команды python3 acl_generator.py https://api.edgecenter.ru/cdn/public_ips_list my-bucket -o policy.json, где my-bucket – наименование бакета в Object Storage.
Пример Python-скрипта
import json
import requests
import argparse
def main():
parser = argparse.ArgumentParser(description='Generate S3 Bucket ACL from IP list')
parser.add_argument('url', help='URL of JSON file with IP addresses')
parser.add_argument('bucket', help='S3 bucket name')
parser.add_argument('-o', '--output', default='acl.json', help='Output filename (default: acl.json)')
args = parser.parse_args()
try:
response = requests.get(args.url, timeout=10)
response.raise_for_status()
data = response.json()
if 'addresses' not in data or not isinstance(data['addresses'], list):
raise ValueError("Invalid JSON structure: 'addresses' array not found")
acl = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": f"arn:aws:s3:::{args.bucket}/*",
"Condition": {
"IpAddress": {
"aws:sourceip": address
}
}
}
for address in data['addresses']
]
}
with open(args.output, 'w') as f:
json.dump(acl, f, indent=4)
print(f"ACL successfully generated for {len(data['addresses'])} IPs. Saved to {args.output}")
except requests.exceptions.RequestException as e:
print(f"Network error: {e}")
except json.JSONDecodeError:
print("Error: Invalid JSON format in response")
except ValueError as e:
print(e)
if __name__ == "__main__":
main()