479 lines
17 KiB
Python
479 lines
17 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2015 Google LLC. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Helper functions for DNS commands."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
from googlecloudsdk.api_lib.dns import util as api_util
|
|
from googlecloudsdk.api_lib.util import apis
|
|
from googlecloudsdk.command_lib.dns import flags
|
|
|
|
import ipaddr
|
|
|
|
|
|
def IsIPv4(ip: str) -> bool:
|
|
"""Returns True if ip is an IPv4."""
|
|
try:
|
|
ipaddr.IPv4Address(ip)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
def IsIPv6(ip: str) -> bool:
|
|
"""Returns True if ip is an IPv6."""
|
|
try:
|
|
ipaddr.IPv6Address(ip)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
def ParseKey(algorithm, key_length, key_type, messages):
|
|
"""Generate a keyspec from the given (unparsed) command line arguments.
|
|
|
|
Args:
|
|
algorithm: (str) String mnemonic for the DNSSEC algorithm to be specified in
|
|
the keyspec; must be a value from AlgorithmValueValuesEnum.
|
|
key_length: (int) The key length value to include in the keyspec.
|
|
key_type: (KeyTypeValueValuesEnum) Enum value for whether to create a
|
|
keyspec for a KSK or a ZSK.
|
|
messages: (module) Module (generally auto-generated by the API build rules)
|
|
containing the API client's message classes.
|
|
|
|
Returns:
|
|
A messages.DnsKeySpec instance created from the given arguments.
|
|
"""
|
|
|
|
key_spec = None
|
|
|
|
if algorithm is not None or key_length is not None:
|
|
spec_args = {}
|
|
spec_args['keyType'] = key_type
|
|
if algorithm is not None:
|
|
spec_args['algorithm'] = messages.DnsKeySpec.AlgorithmValueValuesEnum(
|
|
algorithm)
|
|
if key_length is not None:
|
|
spec_args['keyLength'] = key_length
|
|
|
|
if spec_args:
|
|
key_spec = messages.DnsKeySpec(**spec_args)
|
|
|
|
return key_spec
|
|
|
|
|
|
def ParseDnssecConfigArgs(args, messages, api_version='v1'):
|
|
# TODO(b/215745011) Clean up this function once move to v2
|
|
"""Parse all relevant command line arguments and generate a DNSSEC config.
|
|
|
|
Args:
|
|
args: (dict{str,(str|int)}) Dict of command line arguments; value type
|
|
dependent on particular command line argument.
|
|
messages: (module) Module (generally auto-generated by the API build rules)
|
|
containing the API client's message classes.
|
|
api_version: api_version that this function should use.
|
|
|
|
Returns:
|
|
A messages.ManagedZoneDnsSecConfig instance populated from the given
|
|
command line arguments.
|
|
"""
|
|
|
|
dnssec_config = None
|
|
key_specs = []
|
|
|
|
ksk_algorithm = None
|
|
if args.ksk_algorithm:
|
|
ksk_algorithm = flags.GetKeyAlgorithmFlagMapper(
|
|
'ksk',
|
|
messages,
|
|
).GetEnumForChoice(args.ksk_algorithm)
|
|
zsk_algorithm = None
|
|
if args.zsk_algorithm:
|
|
zsk_algorithm = flags.GetKeyAlgorithmFlagMapper(
|
|
'zsk',
|
|
messages,
|
|
).GetEnumForChoice(args.zsk_algorithm)
|
|
|
|
if api_version == 'v2':
|
|
key_enum = messages.DnsKeySpec.KeyTypeValueValuesEnum.KEY_SIGNING
|
|
else:
|
|
key_enum = messages.DnsKeySpec.KeyTypeValueValuesEnum.keySigning
|
|
|
|
ksk_key = ParseKey(ksk_algorithm, args.ksk_key_length, key_enum, messages)
|
|
if ksk_key is not None:
|
|
key_specs.append(ksk_key)
|
|
|
|
if api_version == 'v2':
|
|
key_enum = messages.DnsKeySpec.KeyTypeValueValuesEnum.ZONE_SIGNING
|
|
else:
|
|
key_enum = messages.DnsKeySpec.KeyTypeValueValuesEnum.zoneSigning
|
|
|
|
zsk_key = ParseKey(zsk_algorithm, args.zsk_key_length, key_enum, messages)
|
|
if zsk_key is not None:
|
|
key_specs.append(zsk_key)
|
|
|
|
dnssec_config_args = {}
|
|
if key_specs:
|
|
dnssec_config_args['defaultKeySpecs'] = key_specs
|
|
if getattr(args, 'denial_of_existence', None) is not None:
|
|
dnssec_config_args['nonExistence'] = (
|
|
flags.GetDoeFlagMapper(messages).GetEnumForChoice(
|
|
args.denial_of_existence))
|
|
if args.dnssec_state is not None:
|
|
dnssec_config_args['state'] = flags.GetDnsSecStateFlagMapper(
|
|
messages, api_version
|
|
).GetEnumForChoice(args.dnssec_state)
|
|
if dnssec_config_args:
|
|
dnssec_config = messages.ManagedZoneDnsSecConfig(**dnssec_config_args)
|
|
|
|
return dnssec_config
|
|
|
|
|
|
def ParseManagedZoneForwardingConfigWithForwardingPath(
|
|
messages, server_list=None, private_server_list=None):
|
|
"""Parses list of forwarding nameservers into ManagedZoneForwardingConfig.
|
|
|
|
Args:
|
|
messages: (module) Module (generally auto-generated by the API build rules)
|
|
containing the API client's message classes.
|
|
server_list: (list) List of IP addresses to use as forwarding targets for
|
|
the DNS Managed Zone that uses default forwarding logic (based on RFC1918
|
|
check).
|
|
private_server_list: (list) List of IP addresses to use as forwarding
|
|
targets for the DNS Managed Zone that always use the private VPC path.
|
|
|
|
Returns:
|
|
A messages.ManagedZoneForwardingConfig instance populated from the given
|
|
command line arguments.
|
|
"""
|
|
target_servers = []
|
|
default_enum = messages.ManagedZoneForwardingConfigNameServerTarget.ForwardingPathValueValuesEnum(
|
|
0)
|
|
private_enum = messages.ManagedZoneForwardingConfigNameServerTarget.ForwardingPathValueValuesEnum(
|
|
1)
|
|
if server_list is not None:
|
|
for name in server_list:
|
|
if IsIPv4(name):
|
|
target_servers.append(
|
|
messages.ManagedZoneForwardingConfigNameServerTarget(
|
|
ipv4Address=name, ipv6Address=None, forwardingPath=default_enum
|
|
)
|
|
)
|
|
elif IsIPv6(name):
|
|
target_servers.append(
|
|
messages.ManagedZoneForwardingConfigNameServerTarget(
|
|
ipv4Address=None, ipv6Address=name, forwardingPath=default_enum
|
|
)
|
|
)
|
|
else:
|
|
target_servers.append(
|
|
messages.ManagedZoneForwardingConfigNameServerTarget(
|
|
ipv4Address=None,
|
|
ipv6Address=None,
|
|
domainName=name,
|
|
forwardingPath=default_enum,
|
|
)
|
|
)
|
|
if private_server_list is not None:
|
|
for name in private_server_list:
|
|
if IsIPv4(name):
|
|
target_servers.append(
|
|
messages.ManagedZoneForwardingConfigNameServerTarget(
|
|
ipv4Address=name, ipv6Address=None,
|
|
forwardingPath=private_enum))
|
|
elif IsIPv6(name):
|
|
target_servers.append(
|
|
messages.ManagedZoneForwardingConfigNameServerTarget(
|
|
ipv4Address=None, ipv6Address=name,
|
|
forwardingPath=private_enum))
|
|
else:
|
|
target_servers.append(
|
|
messages.ManagedZoneForwardingConfigNameServerTarget(
|
|
ipv4Address=None,
|
|
ipv6Address=None,
|
|
domainName=name,
|
|
forwardingPath=private_enum,
|
|
)
|
|
)
|
|
|
|
return messages.ManagedZoneForwardingConfig(targetNameServers=target_servers)
|
|
|
|
|
|
def PolicyNetworkProcessor(parsed_value, version='v1'):
|
|
"""Build PolicyNetwork message from parsed_value."""
|
|
# Parsed Value should be a list of compute.network resources
|
|
messages = GetMessages(version)
|
|
if not parsed_value:
|
|
return []
|
|
|
|
return [
|
|
messages.PolicyNetwork(networkUrl=network_ref.SelfLink())
|
|
for network_ref in parsed_value
|
|
]
|
|
|
|
|
|
def BetaPolicyNetworkProcessor(parsed_value):
|
|
"""Build Beta PolicyNetwork message from parsed_value."""
|
|
# Parsed Value should be a list of compute.network resources
|
|
return PolicyNetworkProcessor(parsed_value, version='v1beta2')
|
|
|
|
|
|
def ResponsePolicyNetworkProcessor(parsed_value, version='v1'):
|
|
"""Build PolicyNetwork message from parsed_value."""
|
|
# Parsed value should be a list of compute.network resources
|
|
messages = GetMessages(version)
|
|
if not parsed_value:
|
|
return []
|
|
|
|
return [
|
|
messages.ResponsePolicyNetwork(networkUrl=network_ref.SelfLink())
|
|
for network_ref in parsed_value
|
|
]
|
|
|
|
def TargetNameServerType(value, version='v1'):
|
|
"""Build a single TargetNameServer based on 'value'.
|
|
|
|
Args:
|
|
value: (str) A string representation of an IPV4 ip address representing the
|
|
PrivateTargetNameServer.
|
|
version: (str) A string indicating the version of the API to be used, should
|
|
be 'v1' only before removing BetaTargetNameServerType.
|
|
|
|
Returns:
|
|
A messages.PolicyAlternativeNameServerConfigTargetNameServer instance
|
|
populated from the given ip address.
|
|
"""
|
|
messages = GetMessages(version)
|
|
if IsIPv4(value):
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=value,
|
|
ipv6Address=None,
|
|
forwardingPath=messages.PolicyAlternativeNameServerConfigTargetNameServer.ForwardingPathValueValuesEnum(
|
|
0
|
|
),
|
|
)
|
|
else:
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=None,
|
|
ipv6Address=value,
|
|
forwardingPath=messages.PolicyAlternativeNameServerConfigTargetNameServer.ForwardingPathValueValuesEnum(
|
|
0
|
|
),
|
|
)
|
|
|
|
|
|
def BetaTargetNameServerType(value, version='v1beta2'):
|
|
"""Build a single TargetNameServer based on 'value'.
|
|
|
|
Args:
|
|
value: (str) A string representation of an IPV4 ip address representing the
|
|
PrivateTargetNameServer.
|
|
version: (str) A string indicating the version of the API to be used, should
|
|
be one of 'v1beta2' or 'v1alpha2'. This function will be removed after
|
|
promoting v6 address to GA.
|
|
|
|
Returns:
|
|
A messages.PolicyAlternativeNameServerConfigTargetNameServer instance
|
|
populated from the given ip address.
|
|
"""
|
|
messages = GetMessages(version)
|
|
if IsIPv4(value):
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=value,
|
|
ipv6Address=None,
|
|
forwardingPath=messages
|
|
.PolicyAlternativeNameServerConfigTargetNameServer
|
|
.ForwardingPathValueValuesEnum(0))
|
|
else:
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=None,
|
|
ipv6Address=value,
|
|
forwardingPath=messages
|
|
.PolicyAlternativeNameServerConfigTargetNameServer
|
|
.ForwardingPathValueValuesEnum(0))
|
|
|
|
|
|
def PrivateTargetNameServerType(value, version='v1'):
|
|
"""Build a single PrivateTargetNameServer based on 'value'.
|
|
|
|
Args:
|
|
value: (str) A string representation of an IPV4 ip address representing the
|
|
PrivateTargetNameServer.
|
|
version: (str) A string indicating the version of the API to be used, should
|
|
be 'v1' only before removing BetaPrivateTargetNameServerType.
|
|
|
|
Returns:
|
|
A messages.PolicyAlternativeNameServerConfigTargetNameServer instance
|
|
populated from the given ip address.
|
|
"""
|
|
messages = GetMessages(version)
|
|
if IsIPv4(value):
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=value,
|
|
ipv6Address=None,
|
|
forwardingPath=messages.PolicyAlternativeNameServerConfigTargetNameServer.ForwardingPathValueValuesEnum(
|
|
1
|
|
),
|
|
)
|
|
else:
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=None,
|
|
ipv6Address=value,
|
|
forwardingPath=messages.PolicyAlternativeNameServerConfigTargetNameServer.ForwardingPathValueValuesEnum(
|
|
1
|
|
),
|
|
)
|
|
|
|
|
|
def BetaPrivateTargetNameServerType(value, version='v1beta2'):
|
|
"""Build a single PrivateTargetNameServer based on 'value'.
|
|
|
|
Args:
|
|
value: (str) A string representation of an IPV4 ip address representing the
|
|
PrivateTargetNameServer.
|
|
version: (str) A string indicating the version of the API to be used, should
|
|
be one of 'v1beta2' or 'v1alpha2'. This function will be removed after
|
|
promoting v6 address to GA.
|
|
|
|
Returns:
|
|
A messages.PolicyAlternativeNameServerConfigTargetNameServer instance
|
|
populated from the given ip address.
|
|
"""
|
|
messages = GetMessages(version)
|
|
if IsIPv4(value):
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=value,
|
|
ipv6Address=None,
|
|
forwardingPath=messages
|
|
.PolicyAlternativeNameServerConfigTargetNameServer
|
|
.ForwardingPathValueValuesEnum(1))
|
|
else:
|
|
return messages.PolicyAlternativeNameServerConfigTargetNameServer(
|
|
ipv4Address=None,
|
|
ipv6Address=value,
|
|
forwardingPath=messages
|
|
.PolicyAlternativeNameServerConfigTargetNameServer
|
|
.ForwardingPathValueValuesEnum(1))
|
|
|
|
|
|
def ParsePolicyNetworks(value, project, version):
|
|
"""Build a list of PolicyNetworks from command line args."""
|
|
networks = ParseNetworks(value, project, version)
|
|
return PolicyNetworkProcessor(networks, version)
|
|
|
|
|
|
def ParseNetworks(value, project, version):
|
|
"""Build a list of PolicyNetworks or ResponsePolicyNetworks from command line args."""
|
|
if not value:
|
|
return []
|
|
registry = api_util.GetRegistry(version)
|
|
networks = [
|
|
registry.Parse(
|
|
network_name,
|
|
collection='compute.networks',
|
|
params={'project': project}) for network_name in value
|
|
]
|
|
return networks
|
|
|
|
|
|
def ParseResponsePolicyNetworks(value, project, version):
|
|
"""Build a list of ResponsePolicyNetworks from command line args."""
|
|
networks = ParseNetworks(value, project, version)
|
|
return ResponsePolicyNetworkProcessor(networks, version)
|
|
|
|
|
|
def ParseAltNameServers(version, server_list=None, private_server_list=None):
|
|
"""Parses list of alternative nameservers into AlternativeNameServerConfig.
|
|
|
|
Args:
|
|
version: (str) A string indicating the version of the API to be used, should
|
|
be 'v1' only before removing BetaParseAltNameServers.
|
|
server_list: (Sequence) List of IP addresses to use as forwarding targets
|
|
for the DNS managed zone that uses default forwarding logic.
|
|
private_server_list: (Sequence) List of IP addresses to use as forwarding
|
|
targets for the DNS Managed Zone that always uses the private VPC path.
|
|
|
|
Returns:
|
|
A messages.PolicyAlternativeNameServerConfig instance populated from the
|
|
given command line arguments.Only the not none server list will be parsed
|
|
and
|
|
an empty list will be returned if both are none.
|
|
"""
|
|
if not server_list and not private_server_list:
|
|
return None
|
|
messages = GetMessages(version)
|
|
result_list = []
|
|
if server_list:
|
|
result_list += [TargetNameServerType(ip, version) for ip in server_list]
|
|
if private_server_list:
|
|
result_list += [
|
|
PrivateTargetNameServerType(ip, version) for ip in private_server_list
|
|
]
|
|
return messages.PolicyAlternativeNameServerConfig(
|
|
targetNameServers=result_list)
|
|
|
|
|
|
def BetaParseAltNameServers(version,
|
|
server_list=None,
|
|
private_server_list=None):
|
|
"""Parses list of alternative nameservers into AlternativeNameServerConfig.
|
|
|
|
Args:
|
|
version: (str) A string indicating the version of the API to be used, should
|
|
be one of 'v1beta2' or 'v1alpha2'. This function will be moved after
|
|
promoting v6 address to GA.
|
|
server_list: (Sequence) List of IP addresses to use as forwarding targets
|
|
for the DNS Managed Zone that uses default forwarding logic.
|
|
private_server_list: (Sequence) List of IP addresses to use as forwarding
|
|
targets for the DNS Managed Zone that always uses the private VPC path.
|
|
|
|
Returns:
|
|
A messages.PolicyAlternativeNameServerConfig instance populated from the
|
|
given command line arguments.Only the not none server list will be parsed
|
|
and
|
|
an empty list will be returned if both are none.
|
|
"""
|
|
if not server_list and not private_server_list:
|
|
return None
|
|
messages = GetMessages(version)
|
|
result_list = []
|
|
if server_list:
|
|
result_list += [BetaTargetNameServerType(ip, version) for ip in server_list]
|
|
if private_server_list:
|
|
result_list += [
|
|
BetaPrivateTargetNameServerType(ip, version)
|
|
for ip in private_server_list
|
|
]
|
|
return messages.PolicyAlternativeNameServerConfig(
|
|
targetNameServers=result_list)
|
|
|
|
|
|
def ParseResponsePolicyRulesBehavior(args, version='v1'):
|
|
"""Parses response policy rule behavior."""
|
|
m = GetMessages(version)
|
|
if args.behavior == 'bypassResponsePolicy':
|
|
return m.ResponsePolicyRule.BehaviorValueValuesEnum.BYPASS_RESPONSE_POLICY if version == 'v2' else m.ResponsePolicyRule.BehaviorValueValuesEnum.bypassResponsePolicy
|
|
else:
|
|
return None
|
|
# TODO(b/215745011) Use this once GCloud is migrated to v2
|
|
# return flags.GetResponsePolicyRulesBehaviorFlagMapper(
|
|
# messages).GetEnumForChoice(args.behavior)
|
|
|
|
|
|
def GetMessages(version='v1'):
|
|
return apis.GetMessagesModule('dns', version)
|