proxies: add smart proxy support

still to do:

* document new configuration options
* add smart proxies to groups view
* import bandwidth and CPU alarms
This commit is contained in:
Iain Learmonth 2022-05-24 19:51:38 +01:00
parent 9b90101cf4
commit 66af6e6550
15 changed files with 275 additions and 32 deletions

View file

@ -12,10 +12,32 @@ from tldextract import tldextract
from app import app
from app.extensions import db
from app.models.base import Group
from app.models.mirrors import Proxy
from app.models.mirrors import Proxy, Origin, SmartProxy
from app.terraform.terraform import TerraformAutomation
def update_smart_proxy_instance(group_id: int,
provider: str,
region: str,
instance_id: str) -> None:
print("SMART PROXY")
instance = SmartProxy.query.filter(
SmartProxy.group_id == group_id,
SmartProxy.region == region,
SmartProxy.provider == provider,
SmartProxy.destroyed.is_(None)
).first()
if instance is None:
instance = SmartProxy()
instance.added = datetime.datetime.utcnow()
instance.group_id = group_id
instance.provider = provider
instance.region = region
db.session.add(instance)
instance.updated = datetime.datetime.utcnow()
instance.instance_id = instance_id
class ProxyAutomation(TerraformAutomation):
subgroup_max = math.inf
"""
@ -35,6 +57,11 @@ class ProxyAutomation(TerraformAutomation):
in the templating of the Terraform configuration.
"""
smart_proxies = False
"""
Whether this provider supports "smart" proxies.
"""
def get_subgroups(self) -> Dict[int, Dict[int, int]]:
conn = db.engine.connect()
result = conn.execute(text("""
@ -118,18 +145,40 @@ class ProxyAutomation(TerraformAutomation):
self.import_state(self.tf_show())
def tf_generate(self) -> None:
groups = Group.query.all()
self.tf_write(
self.template,
groups=Group.query.all(),
groups=groups,
proxies=Proxy.query.filter(
Proxy.provider == self.provider,
Proxy.destroyed.is_(None)
).all(),
subgroups=self.get_subgroups(),
global_namespace=app.config['GLOBAL_NAMESPACE'],
bypass_token=app.config['BYPASS_TOKEN'],
**{
k: app.config[k.upper()]
for k in self.template_parameters
}
)
Proxy.provider == self.provider, Proxy.destroyed.is_(None)).all(), subgroups=self.get_subgroups(),
global_namespace=app.config['GLOBAL_NAMESPACE'], bypass_token=app.config['BYPASS_TOKEN'],
**{k: app.config[k.upper()] for k in self.template_parameters})
if self.smart_proxies:
for group in groups:
self.sp_config(group)
def sp_config(self, group: Group) -> None:
group_origins: List[Origin] = Origin.query.filter(
Origin.group_id == group.id,
Origin.destroyed.is_(None),
Origin.smart.is_(True)
).all()
self.tmpl_write(f"smart_proxy.{group.id}.conf", """
{% for origin in origins %}
server {
listen 443 ssl;
server_name origin-{{ origin.id }}.{{ provider }}.smart.censorship.guide;
location / {
proxy_set_header Accept-Encoding "";
proxy_ssl_server_name on;
proxy_pass https://{{ origin.domain_name }}/;
subs_filter_types text/html text/css text/xml;
subs_filter https://{{ origin.domain_name }}/ /;
}
ssl_certificate /etc/ssl/smart_proxy.crt;
ssl_certificate_key /etc/ssl/private/smart_proxy.key;
}
{% endfor %}
""",
provider=self.provider,
origins=group_origins)

View file

@ -3,34 +3,66 @@ from typing import Any
from app.extensions import db
from app.models.mirrors import Proxy
from app.terraform.proxy import ProxyAutomation
from app.terraform.proxy import ProxyAutomation, update_smart_proxy_instance
class ProxyCloudfrontAutomation(ProxyAutomation):
short_name = "proxy_cloudfront"
description = "Deploy proxies to AWS CloudFront"
provider = "cloudfront"
smart_proxies = True
template_parameters = [
"aws_access_key",
"aws_secret_key"
"aws_secret_key",
"rfc2136_nameserver",
"rfc2136_tsig_key",
"rfc2136_tsig_secret",
"smart_zone"
]
template = """
terraform {
required_providers {
acme = {
source = "vancluever/acme"
version = "~> 2.8.0"
}
aws = {
version = "~> 4.4.0"
}
dns = {
version = "~> 3.2.3"
}
}
}
provider "acme" {
server_url = "https://acme-v02.api.letsencrypt.org/directory"
}
provider "aws" {
access_key = "{{ aws_access_key }}"
secret_key = "{{ aws_secret_key }}"
region = "us-east-2"
}
provider "dns" {
update {
server = local.rfc2136_nameserver
key_name = local.rfc2136_tsig_key
key_secret = local.rfc2136_tsig_secret
key_algorithm = "hmac-sha512"
}
}
locals {
rfc2136_nameserver = "{{ rfc2136_nameserver }}"
rfc2136_tsig_key = "{{ rfc2136_tsig_key }}"
rfc2136_tsig_secret = "{{ rfc2136_tsig_secret }}"
smart_zone = "{{ smart_zone }}"
}
{% for group in groups %}
module "label_{{ group.id }}" {
source = "cloudposse/label/null"
@ -55,13 +87,47 @@ class ProxyCloudfrontAutomation(ProxyAutomation):
resource "aws_sns_topic" "alarms_{{ group.id }}" {
name = "${module.label_{{ group.id }}.id}-cloudfront-alarms"
}
{% for origin in group.origins | selectattr("destroyed", "none") | selectattr("smart") %}
{% if loop.first %}
module "smart_proxy_{{ group.id }}" {
source = "sr2c/bc-smart-proxy-instance/aws"
version = "0.0.1"
context = module.label_{{ group.id }}.context
name = "smart-proxy"
disable_api_termination = false
domain_name = "cloudfront.smart.${local.smart_zone}"
rfc2136_nameserver = local.rfc2136_nameserver
rfc2136_tsig_key = local.rfc2136_tsig_key
rfc2136_tsig_secret = local.rfc2136_tsig_secret
}
resource "aws_s3_object" "smart_config_{{ group.id }}" {
bucket = module.smart_proxy_{{ group.id }}.config_bucket_name
key = "default"
source = "smart_proxy.{{ group.id }}.conf"
etag = filemd5("smart_proxy.{{ group.id }}.conf")
}
{% endif %}
resource "dns_a_record_set" "smart_dns_{{ origin.id }}" {
zone = "{{ smart_zone }}"
name = "origin-{{ origin.id }}.cloudfront.smart"
addresses = module.smart_proxy_{{ origin.group.id }}.ip_addresses
ttl = 60
}
{% endfor %}
{% endfor %}
{% for proxy in proxies %}
module "cloudfront_{{ proxy.id }}" {
source = "sr2c/bc-proxy/aws"
version = "0.0.7"
{% if proxy.origin.smart %}
origin_domain = "origin-{{ proxy.origin.id }}.cloudfront.smart.{{ smart_zone[:-1] }}"
{% else %}
origin_domain = "{{ proxy.origin.domain_name }}"
{% endif %}
logging_bucket = module.log_bucket_{{ proxy.origin.group.id }}.bucket_domain_name
sns_topic_arn = aws_sns_topic.alarms_{{ proxy.origin.group.id }}.arn
low_bandwidth_alarm = false
@ -88,4 +154,12 @@ class ProxyCloudfrontAutomation(ProxyAutomation):
proxy.slug = res['values']['id']
proxy.terraform_updated = datetime.datetime.utcnow()
break
for g in state["values"]["root_module"]["child_modules"]:
if g["address"].startswith("module.smart_proxy_"):
group_id = int(g["address"][len("module.smart_proxy_"):])
for s in g["child_modules"]:
if s["address"].endswith(".module.instance"):
for x in s["resources"]:
if x["address"].endswith(".module.instance.aws_instance.default[0]"):
update_smart_proxy_instance(group_id, self.provider, "us-east-2", x['values']['id'])
db.session.commit()