diff --git a/debugging/mariadb.build.md b/debugging/mariadb.build.md index 10034862e9..261a11096d 100644 --- a/debugging/mariadb.build.md +++ b/debugging/mariadb.build.md @@ -176,6 +176,7 @@ usermod -aG frappe www-data ```nginx echo "server { listen 80; + listen [::]:80; server_name packages.frappe.cloud; location / { diff --git a/deployment/nginx.conf b/deployment/nginx.conf index 8b528b1b2a..254224fdfe 100644 --- a/deployment/nginx.conf +++ b/deployment/nginx.conf @@ -9,13 +9,15 @@ upstream frappe-bench-socketio-server { server { - listen 80 default_server; + listen 80 default_server; + listen [::]:80 default_server; server_name ""; return 444; } server { listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; server_name ""; ssl on; @@ -42,6 +44,7 @@ map $host $site_name_sxjfjnv { server { listen 443 ssl http2; + listen [::]:443 ssl http2; server_name @@ -75,6 +78,7 @@ server { return 301 https://$host$request_uri; } # managed by Certbot listen 80; + listen [::]:80; server_name frappe.cloud ; @@ -88,6 +92,7 @@ proxy_cache_path /var/cache/nginx/assets keys_zone=assets_cache:10m loader_thres server { listen 443 ssl http2; + listen [::]:443 ssl http2; server_name @@ -431,6 +436,7 @@ server { listen 80; + listen [::]:80; server_name frappecloud.com ; diff --git a/press/api/site.py b/press/api/site.py index 582399fd26..4140148b51 100644 --- a/press/api/site.py +++ b/press/api/site.py @@ -1615,6 +1615,37 @@ def check_dns_a(name, domain): return result +def check_dns_aaaa(name, domain): + result = {"type": "AAAA", "matched": False, "answer": ""} + try: + resolver = Resolver(configure=False) + resolver.nameservers = NAMESERVERS + answer = resolver.query(domain, "AAAA") + domain_ip = answer[0].to_text() + site_ip = resolver.query(name, "AAAA")[0].to_text() + result["answer"] = answer.rrset.to_text() + if domain_ip == site_ip: + result["matched"] = True + elif site_ip: + # We can issue certificates even if the domain points to the secondary proxies + server = frappe.db.get_value("Site", name, "server") + proxy = frappe.db.get_value("Server", server, "proxy_server") + secondary_ips = frappe.get_all( + "Proxy Server", + {"status": "Active", "primary": proxy, "is_replication_setup": True}, + pluck="ip6", + ) + if domain_ip in secondary_ips: + result["matched"] = True + except dns.exception.DNSException as e: + result["answer"] = str(e) + except Exception as e: + result["answer"] = str(e) + log_error("DNS Query Exception - AAAA", site=name, domain=domain, exception=e) + finally: + return result + + def ensure_dns_aaaa_record_doesnt_exist(domain: str): """ Ensure that the domain doesn't have an AAAA record @@ -1639,7 +1670,7 @@ def ensure_dns_aaaa_record_doesnt_exist(domain: str): def check_dns_cname_a(name, domain): check_domain_allows_letsencrypt_certs(domain) - ensure_dns_aaaa_record_doesnt_exist(domain) + # ensure_dns_aaaa_record_doesnt_exist(domain) cname = check_dns_cname(name, domain) result = {"CNAME": cname} result.update(cname) @@ -1651,6 +1682,15 @@ def check_dns_cname_a(name, domain): result.update({"A": a}) result.update(a) + # Check that both A and AAAA records match a proxy + aaaa = check_dns_aaaa(name, domain) + result.update({"AAAA": aaaa}) + a_found = a["answer"] and "does not contain an answer" not in a["answer"] + aaaa_found = aaaa["answer"] and "does not contain an answer" not in aaaa["answer"] + if a_found and aaaa_found and a["matched"] != aaaa["matched"]: + # There is both records but one does not match. + result["matched"] = False + return result diff --git a/press/docker/registry.conf b/press/docker/registry.conf index 953e2b75b8..048466c37f 100644 --- a/press/docker/registry.conf +++ b/press/docker/registry.conf @@ -17,6 +17,7 @@ map $upstream_http_docker_distribution_api_version $docker_distribution_api_vers server { listen 443 ssl; + listen [::]:443 ssl; server_name registry.frappe.cloud; # SSL diff --git a/press/playbooks/roles/ssl_nginx/templates/ssl.conf b/press/playbooks/roles/ssl_nginx/templates/ssl.conf index 301a9119a2..30b3cb4d55 100644 --- a/press/playbooks/roles/ssl_nginx/templates/ssl.conf +++ b/press/playbooks/roles/ssl_nginx/templates/ssl.conf @@ -1,5 +1,6 @@ server { listen 80; + listen [::]:80; server_name {{ domain }}; location ^~ /.well-known/acme-challenge/ { diff --git a/press/press/doctype/analytics_server/analytics_server.json b/press/press/doctype/analytics_server/analytics_server.json index 6bd32c291b..d97d19a3aa 100644 --- a/press/press/doctype/analytics_server/analytics_server.json +++ b/press/press/doctype/analytics_server/analytics_server.json @@ -14,6 +14,7 @@ "is_server_setup", "networking_section", "ip", + "ip6", "column_break_10", "private_ip", "private_mac_address", @@ -107,6 +108,13 @@ "reqd": 1, "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fieldname": "column_break_10", "fieldtype": "Column Break" @@ -252,7 +260,7 @@ "link_fieldname": "server" } ], - "modified": "2023-12-13 15:09:40.978998", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Analytics Server", diff --git a/press/press/doctype/analytics_server/analytics_server.py b/press/press/doctype/analytics_server/analytics_server.py index b4bc1c93d6..68e2357b40 100644 --- a/press/press/doctype/analytics_server/analytics_server.py +++ b/press/press/doctype/analytics_server/analytics_server.py @@ -28,6 +28,7 @@ class AnalyticsServer(BaseServer): google_client_secret: DF.Password | None hostname: DF.Data ip: DF.Data + ip6: DF.Data | None is_server_setup: DF.Check monitoring_password: DF.Password | None plausible_mail_login: DF.Data | None diff --git a/press/press/doctype/app_release/code.md b/press/press/doctype/app_release/code.md index 7737e8bf1b..ab0b5ec01b 100644 --- a/press/press/doctype/app_release/code.md +++ b/press/press/doctype/app_release/code.md @@ -10,6 +10,7 @@ docker run -it -p 127.0.0.1:8021:8080 \ ``` server { listen 80; + listen [::]:80; server_name code.staging.frappe.cloud; location / { proxy_pass http://127.0.0.1:8021; diff --git a/press/press/doctype/database_server/database_server.json b/press/press/doctype/database_server/database_server.json index a6e9b86534..6d93c73e1b 100644 --- a/press/press/doctype/database_server/database_server.json +++ b/press/press/doctype/database_server/database_server.json @@ -28,6 +28,7 @@ "auto_add_storage_max", "networking_section", "ip", + "ip6", "column_break_10", "private_ip", "private_mac_address", @@ -104,6 +105,13 @@ "label": "IP", "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fetch_from": "virtual_machine.private_ip_address", "fieldname": "private_ip", @@ -521,7 +529,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-08-13 11:02:07.399141", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Database Server", diff --git a/press/press/doctype/database_server/database_server.py b/press/press/doctype/database_server/database_server.py index b5bcd197c4..1edb865676 100644 --- a/press/press/doctype/database_server/database_server.py +++ b/press/press/doctype/database_server/database_server.py @@ -42,6 +42,7 @@ class DatabaseServer(BaseServer): hostname: DF.Data hostname_abbreviation: DF.Data | None ip: DF.Data | None + ip6: DF.Data | None is_performance_schema_enabled: DF.Check is_primary: DF.Check is_replication_setup: DF.Check diff --git a/press/press/doctype/log_server/log_server.json b/press/press/doctype/log_server/log_server.json index a86cc4d449..3cd1070b1a 100644 --- a/press/press/doctype/log_server/log_server.json +++ b/press/press/doctype/log_server/log_server.json @@ -15,6 +15,7 @@ "is_server_setup", "networking_section", "ip", + "ip6", "column_break_9", "private_ip", "private_mac_address", @@ -90,6 +91,13 @@ "label": "IP", "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fieldname": "column_break_9", "fieldtype": "Column Break" @@ -198,7 +206,7 @@ "link_fieldname": "server" } ], - "modified": "2023-12-13 15:09:14.473225", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Log Server", diff --git a/press/press/doctype/log_server/log_server.py b/press/press/doctype/log_server/log_server.py index afd963fa52..5d814991f8 100644 --- a/press/press/doctype/log_server/log_server.py +++ b/press/press/doctype/log_server/log_server.py @@ -24,6 +24,7 @@ class LogServer(BaseServer): frappe_user_password: DF.Password | None hostname: DF.Data ip: DF.Data | None + ip6: DF.Data | None is_server_setup: DF.Check kibana_password: DF.Password | None monitoring_password: DF.Password | None diff --git a/press/press/doctype/monitor_server/monitor_server.json b/press/press/doctype/monitor_server/monitor_server.json index bd2f4ccd01..51c82b7d7e 100644 --- a/press/press/doctype/monitor_server/monitor_server.json +++ b/press/press/doctype/monitor_server/monitor_server.json @@ -15,6 +15,7 @@ "is_server_setup", "networking_section", "ip", + "ip6", "column_break_9", "private_ip", "private_mac_address", @@ -92,6 +93,13 @@ "label": "IP", "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fieldname": "column_break_9", "fieldtype": "Column Break" @@ -212,7 +220,7 @@ "link_fieldname": "server" } ], - "modified": "2024-02-05 20:07:19.024804", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Monitor Server", diff --git a/press/press/doctype/monitor_server/monitor_server.py b/press/press/doctype/monitor_server/monitor_server.py index c33a341a7b..fde99daff8 100644 --- a/press/press/doctype/monitor_server/monitor_server.py +++ b/press/press/doctype/monitor_server/monitor_server.py @@ -28,6 +28,7 @@ class MonitorServer(BaseServer): grafana_password: DF.Password | None hostname: DF.Data ip: DF.Data | None + ip6: DF.Data | None is_server_setup: DF.Check monitoring_password: DF.Password | None private_ip: DF.Data diff --git a/press/press/doctype/proxy_server/proxy_server.json b/press/press/doctype/proxy_server/proxy_server.json index 6b1b73e31d..504d43a702 100644 --- a/press/press/doctype/proxy_server/proxy_server.json +++ b/press/press/doctype/proxy_server/proxy_server.json @@ -20,6 +20,7 @@ "public", "section_break_8", "ip", + "ip6", "enabled_default_routing", "column_break_10", "private_ip", @@ -70,6 +71,13 @@ "label": "IP", "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fetch_from": "virtual_machine.private_ip_address", "fieldname": "private_ip", @@ -400,7 +408,7 @@ } ], "links": [], - "modified": "2024-09-10 15:44:10.989216", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Proxy Server", diff --git a/press/press/doctype/proxy_server/proxy_server.py b/press/press/doctype/proxy_server/proxy_server.py index f2d77c5f81..e07c22c38c 100644 --- a/press/press/doctype/proxy_server/proxy_server.py +++ b/press/press/doctype/proxy_server/proxy_server.py @@ -41,6 +41,7 @@ class ProxyServer(BaseServer): hostname: DF.Data hostname_abbreviation: DF.Data | None ip: DF.Data | None + ip6: DF.Data | None is_primary: DF.Check is_proxysql_setup: DF.Check is_replication_setup: DF.Check diff --git a/press/press/doctype/registry_server/registry_server.json b/press/press/doctype/registry_server/registry_server.json index f576e20baa..cfd831974d 100644 --- a/press/press/doctype/registry_server/registry_server.json +++ b/press/press/doctype/registry_server/registry_server.json @@ -14,6 +14,7 @@ "is_server_setup", "networking_section", "ip", + "ip6", "column_break_9", "private_ip", "private_mac_address", @@ -75,6 +76,13 @@ "reqd": 1, "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fetch_from": "virtual_machine.private_ip_address", "fieldname": "private_ip", @@ -204,7 +212,7 @@ "link_fieldname": "server" } ], - "modified": "2023-12-13 15:09:46.909110", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Registry Server", diff --git a/press/press/doctype/registry_server/registry_server.py b/press/press/doctype/registry_server/registry_server.py index e427fadd7d..7ee0708fb2 100644 --- a/press/press/doctype/registry_server/registry_server.py +++ b/press/press/doctype/registry_server/registry_server.py @@ -26,6 +26,7 @@ class RegistryServer(BaseServer): frappe_user_password: DF.Password | None hostname: DF.Data ip: DF.Data + ip6: DF.Data | None is_server_setup: DF.Check monitoring_password: DF.Password | None private_ip: DF.Data diff --git a/press/press/doctype/self_hosted_server/self_hosted_server.json b/press/press/doctype/self_hosted_server/self_hosted_server.json index 8a3d213bb1..2eb6ea7bff 100644 --- a/press/press/doctype/self_hosted_server/self_hosted_server.json +++ b/press/press/doctype/self_hosted_server/self_hosted_server.json @@ -22,6 +22,7 @@ "server_created", "server", "ip", + "ip6", "ssh_user", "column_break_smwr", "existing_bench_present", @@ -95,6 +96,12 @@ "label": "IP", "reqd": 1 }, + { + "fieldname": "ip6", + "fieldtype": "Data", + "in_list_view": 1, + "label": "IPv6" + }, { "default": "root", "fetch_from": ".ssh_user", @@ -451,7 +458,7 @@ } ], "links": [], - "modified": "2024-05-29 11:41:41.304954", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Self Hosted Server", diff --git a/press/press/doctype/self_hosted_server/self_hosted_server.py b/press/press/doctype/self_hosted_server/self_hosted_server.py index a40d4c651d..67955f9051 100644 --- a/press/press/doctype/self_hosted_server/self_hosted_server.py +++ b/press/press/doctype/self_hosted_server/self_hosted_server.py @@ -49,6 +49,7 @@ class SelfHostedServer(Document): hostname: DF.Data | None instance_type: DF.Data | None ip: DF.Data + ip6: DF.Data | None is_managed_database: DF.Check mariadb_ip: DF.Data | None mariadb_private_ip: DF.Data | None diff --git a/press/press/doctype/server/server.json b/press/press/doctype/server/server.json index ba70493f3d..c48f413cdf 100644 --- a/press/press/doctype/server/server.json +++ b/press/press/doctype/server/server.json @@ -29,6 +29,7 @@ "auto_add_storage_max", "networking_section", "ip", + "ip6", "column_break_3", "private_ip", "private_mac_address", @@ -87,6 +88,13 @@ "label": "IP", "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fieldname": "proxy_server", "fieldtype": "Link", @@ -526,7 +534,7 @@ } ], "links": [], - "modified": "2024-09-24 12:56:06.423649", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Server", diff --git a/press/press/doctype/server/server.py b/press/press/doctype/server/server.py index e989006d93..4645645b57 100644 --- a/press/press/doctype/server/server.py +++ b/press/press/doctype/server/server.py @@ -1102,6 +1102,7 @@ class Server(BaseServer): hostname_abbreviation: DF.Data | None ignore_incidents_since: DF.Datetime | None ip: DF.Data | None + ip6: DF.Data | None is_managed_database: DF.Check is_primary: DF.Check is_replication_setup: DF.Check diff --git a/press/press/doctype/site_domain/site_domain.py b/press/press/doctype/site_domain/site_domain.py index 71f38ca31c..9eff6efaf6 100644 --- a/press/press/doctype/site_domain/site_domain.py +++ b/press/press/doctype/site_domain/site_domain.py @@ -204,9 +204,10 @@ def update_dns_type(): return try: response = check_dns(domain.site, domain.domain) - if response["matched"] and response["type"] != domain.dns_type: + dns_type = "A" if response["type"] == "AAAA" else response["type"] + if response["matched"] and dns_type != domain.dns_type: frappe.db.set_value( - "Site Domain", domain.name, "dns_type", response["type"], update_modified=False + "Site Domain", domain.name, "dns_type", dns_type, update_modified=False ) pretty_response = json.dumps(response, indent=4, default=str) frappe.db.set_value( diff --git a/press/press/doctype/trace_server/trace_server.json b/press/press/doctype/trace_server/trace_server.json index 4225c8ff55..1e34b77383 100644 --- a/press/press/doctype/trace_server/trace_server.json +++ b/press/press/doctype/trace_server/trace_server.json @@ -14,6 +14,7 @@ "is_server_setup", "networking_section", "ip", + "ip6", "column_break_10", "private_ip", "private_mac_address", @@ -109,6 +110,13 @@ "reqd": 1, "set_only_once": 1 }, + { + "fetch_from": "virtual_machine.public_ip6_address", + "fieldname": "ip6", + "fieldtype": "Data", + "label": "IPv6", + "set_only_once": 1 + }, { "fieldname": "column_break_10", "fieldtype": "Column Break" @@ -267,7 +275,7 @@ "link_fieldname": "server" } ], - "modified": "2023-12-13 15:09:34.499141", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Trace Server", diff --git a/press/press/doctype/trace_server/trace_server.py b/press/press/doctype/trace_server/trace_server.py index 1e433fb7c8..46b38933cf 100644 --- a/press/press/doctype/trace_server/trace_server.py +++ b/press/press/doctype/trace_server/trace_server.py @@ -23,6 +23,7 @@ class TraceServer(BaseServer): frappe_user_password: DF.Password | None hostname: DF.Data ip: DF.Data + ip6: DF.Data | None is_server_setup: DF.Check monitoring_password: DF.Password | None private_ip: DF.Data diff --git a/press/press/doctype/virtual_machine/virtual_machine.json b/press/press/doctype/virtual_machine/virtual_machine.json index 6dc0986146..983ea4f35b 100644 --- a/press/press/doctype/virtual_machine/virtual_machine.json +++ b/press/press/doctype/virtual_machine/virtual_machine.json @@ -34,6 +34,7 @@ "subnet_id", "private_ip_address", "public_ip_address", + "public_ip6_address", "column_break_15", "subnet_cidr_block", "public_dns_name", @@ -149,6 +150,12 @@ "label": "Public IP Address", "read_only": 1 }, + { + "fieldname": "public_ip6_address", + "fieldtype": "Data", + "label": "Public IPv6 Address", + "read_only": 1 + }, { "fieldname": "column_break_15", "fieldtype": "Column Break" @@ -323,7 +330,7 @@ "link_fieldname": "virtual_machine" } ], - "modified": "2024-10-09 10:54:27.750679", + "modified": "2024-10-15 15:28:10.193273", "modified_by": "Administrator", "module": "Press", "name": "Virtual Machine", diff --git a/press/press/doctype/virtual_machine/virtual_machine.py b/press/press/doctype/virtual_machine/virtual_machine.py index 13d4db3d92..f27127957f 100644 --- a/press/press/doctype/virtual_machine/virtual_machine.py +++ b/press/press/doctype/virtual_machine/virtual_machine.py @@ -70,6 +70,7 @@ class VirtualMachine(Document): private_ip_address: DF.Data | None public_dns_name: DF.Data | None public_ip_address: DF.Data | None + public_ip6_address: DF.Data | None ram: DF.Int region: DF.Link security_group_id: DF.Data | None @@ -159,6 +160,7 @@ def _provision_hetzner(self): self.private_ip_address = server.private_net[0].ip self.public_ip_address = server.public_net.ipv4.ip + self.public_ip6_address = server.public_net.ipv6.ip self.instance_id = server.id @@ -497,6 +499,7 @@ def _sync_hetzner(self, server_instance=None): self.machine_type = server_instance.server_type.name self.private_ip_address = server_instance.private_net[0].ip self.public_ip_address = server_instance.public_net.ipv4.ip + self.public_ip6_address = server_instance.public_net.ipv6.ip else: self.status = "Terminated" self.save() @@ -520,7 +523,7 @@ def _sync_oci(self, instance=None): # noqa: C901 ): try: vnic = self.client(VirtualNetworkClient).get_vnic(vnic_id=vnic_attachment.vnic_id).data - self.public_ip_address = vnic.public_ip + self.public_ip_address = vnic.public_ip # TODO: IPv6 support except Exception: log_error( title="OCI VNIC Fetch Error", @@ -585,6 +588,7 @@ def _sync_aws(self, response=None): # noqa: C901 self.machine_type = instance.get("InstanceType") self.public_ip_address = instance.get("PublicIpAddress") + self.public_ip6_address = instance.get("Ipv6Address") self.private_ip_address = instance.get("PrivateIpAddress") self.public_dns_name = instance.get("PublicDnsName") diff --git a/press/runner.py b/press/runner.py index 02620ed294..aa0d6952f0 100644 --- a/press/runner.py +++ b/press/runner.py @@ -149,6 +149,8 @@ def __init__(self, server, playbook, user="root", variables=None, port=22): self.playbook = playbook self.playbook_path = frappe.get_app_path("press", "playbooks", self.playbook) self.host = f"{server.ip}:{port}" + if server.ip6: + self.host = f"[{server.ip6}]:{port}" self.variables = variables or {} constants.HOST_KEY_CHECKING = False