Created
June 22, 2025 01:25
-
-
Save sivachandran/b62c932ab331a256be5a261e870aa72e to your computer and use it in GitHub Desktop.
Python script to update OCI security list to whitelist dynamic dns ip address for SSH access.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import oci | |
import sys | |
# === Config - Update these === | |
DOMAIN_NAME = "example.com" | |
SECURITY_LIST_ID = "ocid1.securitylist.oc1..xxxxxx" # Replace with your Security List OCID | |
PORT = 22 | |
PROTOCOL = "6" # TCP | |
DESCRIPTION = f"SSH access for {DOMAIN_NAME}" | |
def resolve_domain_to_ip(domain): | |
try: | |
ip = socket.gethostbyname(domain) | |
print(f"[+] Resolved {domain} to {ip}") | |
return ip | |
except socket.gaierror as e: | |
print(f"[-] Failed to resolve domain {domain}: {e}") | |
sys.exit(1) | |
def get_security_list(client, sec_list_id): | |
return client.get_security_list(sec_list_id).data | |
def find_rule_by_description(rules, description): | |
""" | |
Finds the ingress rule matching the description. | |
Returns the rule and its index. | |
""" | |
for idx, rule in enumerate(rules): | |
if rule.description == description: | |
return idx, rule | |
return None, None | |
def main(): | |
ip = resolve_domain_to_ip(DOMAIN_NAME) | |
ip_cidr = f"{ip}/32" | |
signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner() | |
client = oci.core.VirtualNetworkClient(config={}, signer=signer) | |
sec_list = get_security_list(client, SECURITY_LIST_ID) | |
ingress_rules = list(sec_list.ingress_security_rules) | |
idx, existing_rule = find_rule_by_description(ingress_rules, DESCRIPTION) | |
if existing_rule: | |
if existing_rule.source == ip_cidr and existing_rule.protocol == PROTOCOL: | |
port_range = existing_rule.tcp_options.destination_port_range if existing_rule.tcp_options else None | |
if port_range and port_range.min == PORT and port_range.max == PORT: | |
print("[*] Existing rule with same IP and port found. No update needed.") | |
return | |
# IP changed — remove the old rule | |
print(f"[!] IP change detected. Removing old rule with source {existing_rule.source}") | |
ingress_rules.pop(idx) | |
# Add new rule with updated IP | |
new_rule = oci.core.models.IngressSecurityRule( | |
protocol=PROTOCOL, | |
source=ip_cidr, | |
source_type="CIDR_BLOCK", | |
tcp_options=oci.core.models.TcpOptions( | |
destination_port_range=oci.core.models.PortRange(min=PORT, max=PORT) | |
), | |
description=DESCRIPTION | |
) | |
ingress_rules.append(new_rule) | |
update_details = oci.core.models.UpdateSecurityListDetails( | |
ingress_security_rules=ingress_rules, | |
egress_security_rules=sec_list.egress_security_rules, | |
defined_tags=sec_list.defined_tags, | |
freeform_tags=sec_list.freeform_tags, | |
display_name=sec_list.display_name | |
) | |
client.update_security_list( | |
security_list_id=SECURITY_LIST_ID, | |
update_security_list_details=update_details | |
) | |
print(f"[+] Updated security list with new IP {ip_cidr} for description '{DESCRIPTION}'.") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment