
    Vh>^                     <   d dl mZmZmZ eZdZdZdZd dl	Z	d dl
mZmZ d dlmZmZmZmZmZ d dlmZ d d	lmZmZ 	 d dlZd
ZdZg dZddgZd Z d Z!ddZ"d Z#d Z$d Z%d Z&d Z'd Z(d Z)e*dk(  r e)        yy# e$ rZ e	j:                         ZdZY dZ[RdZ[ww xY w)    )absolute_importdivisionprint_functiona  
module: firewall
short_description: Manage Hetzner's dedicated server firewall
author:
  - Felix Fontein (@felixfontein)
description:
  - Manage Hetzner's dedicated server firewall.
  - Note that idempotency check for TCP flags simply compares strings and does not try to interpret the rules. This might
    change in the future.
requirements:
  - ipaddress
seealso:
  - name: Firewall documentation
    description: Hetzner's documentation on the stateless firewall for dedicated servers.
    link: https://docs.hetzner.com/robot/dedicated-server/firewall/
  - module: community.hrobot.firewall_info
    description: Retrieve information on firewall configuration.
extends_documentation_fragment:
  - community.hrobot.robot
  - community.hrobot.attributes
  - community.hrobot.attributes.actiongroup_robot

attributes:
  action_group:
    version_added: 1.6.0
  check_mode:
    support: full
  diff_mode:
    support: full
  idempotent:
    support: full

options:
  server_ip:
    description:
      - The server's main IP address.
      - Exactly one of O(server_ip) and O(server_number) must be specified.
      - Note that Hetzner deprecated identifying the server's firewall by the server's main IP. Using this option can thus
        stop working at any time in the future. Use O(server_number) instead.
    type: str
  server_number:
    description:
      - The server's number.
      - Exactly one of O(server_ip) and O(server_number) must be specified.
    type: int
    version_added: 1.8.0
  filter_ipv6:
    description:
      - Whether to filter IPv6 traffic as well.
      - IPv4 traffic is always filtered, IPv6 traffic filtering needs to be explicitly enabled.
    type: bool
    version_added: 1.8.0
  port:
    description:
      - Switch port of firewall.
    type: str
    choices: [main, kvm]
    default: main
  state:
    description:
      - Status of the firewall.
      - Firewall is active if state is V(present), and disabled if state is V(absent).
    type: str
    default: present
    choices: [present, absent]
  allowlist_hos:
    description:
      - Whether Hetzner services have access.
    type: bool
    aliases:
      - whitelist_hos
  rules:
    description:
      - Firewall rules.
    type: dict
    suboptions:
      input:
        description:
          - Input firewall rules.
        type: list
        elements: dict
        suboptions:
          name:
            description:
              - Name of the firewall rule.
              - Note that Hetzner restricts the characters that can be used for rule names. At the moment, only letters C(a-z),
                C(A-Z), space, and the symbols C(.), C(-), C(+), C(_), and C(@) are allowed.
            type: str
          ip_version:
            description:
              - Internet protocol version.
              - Leave away to filter both protocols. Note that in that case, none of O(rules.input[].dst_ip), O(rules.input[].src_ip),
                or O(rules.input[].protocol) can be specified.
            type: str
          dst_ip:
            description:
              - Destination IP address or subnet address.
              - CIDR notation.
            type: str
          dst_port:
            description:
              - Destination port or port range.
            type: str
          src_ip:
            description:
              - Source IP address or subnet address.
              - CIDR notation.
            type: str
          src_port:
            description:
              - Source port or port range.
            type: str
          protocol:
            description:
              - Protocol above IP layer.
            type: str
          tcp_flags:
            description:
              - TCP flags or logical combination of flags.
              - Flags supported by Hetzner are V(syn), V(fin), V(rst), V(psh) and V(urg).
              - They can be combined with V(|) (logical or) and V(&) (logical and).
              - See L(the documentation,https://wiki.hetzner.de/index.php/Robot_Firewall/en#Parameter) for more information.
            type: str
          action:
            description:
              - Action if rule matches.
            required: true
            type: str
            choices: [accept, discard]
      output:
        description:
          - Output firewall rules.
        type: list
        elements: dict
        version_added: 1.8.0
        suboptions:
          name:
            description:
              - Name of the firewall rule.
              - Note that Hetzner restricts the characters that can be used for rule names. At the moment, only letters C(a-z),
                C(A-Z), space, and the symbols C(.), C(-), C(+), C(_), and C(@) are allowed.
            type: str
          ip_version:
            description:
              - Internet protocol version.
              - Leave away to filter both protocols. Note that in that case, none of O(rules.output[].dst_ip), O(rules.output[].src_ip),
                or O(rules.output[].protocol) can be specified.
            type: str
          dst_ip:
            description:
              - Destination IP address or subnet address.
              - CIDR notation.
            type: str
          dst_port:
            description:
              - Destination port or port range.
            type: str
          src_ip:
            description:
              - Source IP address or subnet address.
              - CIDR notation.
            type: str
          src_port:
            description:
              - Source port or port range.
            type: str
          protocol:
            description:
              - Protocol above IP layer.
            type: str
          tcp_flags:
            description:
              - TCP flags or logical combination of flags.
              - Flags supported by Hetzner are V(syn), V(fin), V(rst), V(psh) and V(urg).
              - They can be combined with V(|) (logical or) and V(&) (logical and).
              - See L(the documentation,https://wiki.hetzner.de/index.php/Robot_Firewall/en#Parameter) for more information.
            type: str
          action:
            description:
              - Action if rule matches.
            required: true
            type: str
            choices: [accept, discard]
  update_timeout:
    description:
      - Timeout to use when configuring the firewall.
      - Note that the API call returns before the firewall has been successfully set up.
    type: int
    default: 30
  wait_for_configured:
    description:
      - Whether to wait until the firewall has been successfully configured before determining what to do, and before returning
        from the module.
      - The API returns status C(in progress) when the firewall is currently being configured. If this happens, the module
        will try again until the status changes to C(active) or C(disabled).
      - Please note that there is a request limit. If you have to do multiple updates, it can be better to disable waiting,
        and regularly use M(community.hrobot.firewall_info) to query status.
    type: bool
    default: true
  wait_delay:
    description:
      - Delay to wait (in seconds) before checking again whether the firewall has been configured.
    type: int
    default: 10
  timeout:
    description:
      - Timeout (in seconds) for waiting for firewall to be configured.
    type: int
    default: 180
a  
---
- name: Configure firewall for server with main IP 1.2.3.4
  community.hrobot.firewall:
    hetzner_user: foo
    hetzner_password: bar
    server_ip: 1.2.3.4
    state: present
    filter_ipv6: true
    allowlist_hos: true
    rules:
      input:
        - name: Allow ICMP protocol
          # This is needed so you can ping your server
          ip_version: ipv4
          protocol: icmp
          action: accept
          # Note that it is not possible to disable ICMP for IPv6
          # (https://robot.hetzner.com/doc/webservice/en.html#post-firewall-server-id)
        - name: Allow responses to incoming TCP connections
          protocol: tcp
          dst_port: '32768-65535'
          tcp_flags: ack
          action: accept
        - name: Allow restricted access from some known IPv4 addresses
          # Allow everything to ports 20-23 from 4.3.2.1/24 (IPv4 only)
          ip_version: ipv4
          src_ip: 4.3.2.1/24
          dst_port: '20-23'
          action: accept
        - name: Allow everything to port 443
          dst_port: '443'
          action: accept
        - name: Drop everything else
          action: discard
      output:
        - name: Accept everything
          action: accept
  register: result

- ansible.builtin.debug:
    msg: "{{ result }}"
a[  
firewall:
  description:
    - The firewall configuration.
  type: dict
  returned: success
  contains:
    port:
      description:
        - Switch port of firewall.
        - V(main) or V(kvm).
      type: str
      sample: main
    server_ip:
      description:
        - Server's main IP address.
      type: str
      sample: 1.2.3.4
    server_number:
      description:
        - Hetzner's internal server number.
      type: int
      sample: 12345
    status:
      description:
        - Status of the firewall.
        - V(active) or V(disabled).
        - Will be V(in process) if the firewall is currently updated, and O(wait_for_configured) is set to V(false) or O(timeout)
          to a too small value.
      type: str
      sample: active
    allowlist_hos:
      description:
        - Whether Hetzner services have access.
      type: bool
      sample: true
      version_added: 1.2.0
    whitelist_hos:
      description:
        - Whether Hetzner services have access.
        - Old name of return value V(allowlist_hos), will be removed eventually.
      type: bool
      sample: true
    rules:
      description:
        - Firewall rules.
      type: dict
      contains:
        input:
          description:
            - Input firewall rules.
          type: list
          elements: dict
          contains:
            name:
              description:
                - Name of the firewall rule.
              type: str
              sample: Allow HTTP access to server
            ip_version:
              description:
                - Internet protocol version.
                - No value means the rule applies both to IPv4 and IPv6.
              type: str
              sample: ipv4
            dst_ip:
              description:
                - Destination IP address or subnet address.
                - CIDR notation.
              type: str
              sample: 1.2.3.4/32
            dst_port:
              description:
                - Destination port or port range.
              type: str
              sample: "443"
            src_ip:
              description:
                - Source IP address or subnet address.
                - CIDR notation.
              type: str
              sample:
            src_port:
              description:
                - Source port or port range.
              type: str
              sample:
            protocol:
              description:
                - Protocol above IP layer.
              type: str
              sample: tcp
            tcp_flags:
              description:
                - TCP flags or logical combination of flags.
              type: str
              sample:
            action:
              description:
                - Action if rule matches.
                - V(accept) or V(discard).
              type: str
              sample: accept
              choices:
                - accept
                - discard
        output:
          description:
            - Output firewall rules.
          type: list
          elements: dict
          contains:
            name:
              description:
                - Name of the firewall rule.
              type: str
              sample: Allow HTTP access to server
            ip_version:
              description:
                - Internet protocol version.
                - No value means the rule applies both to IPv4 and IPv6.
              type: str
              sample:
            dst_ip:
              description:
                - Destination IP address or subnet address.
                - CIDR notation.
              type: str
              sample: 1.2.3.4/32
            dst_port:
              description:
                - Destination port or port range.
              type: str
              sample: "443"
            src_ip:
              description:
                - Source IP address or subnet address.
                - CIDR notation.
              type: str
              sample:
            src_port:
              description:
                - Source port or port range.
              type: str
              sample:
            protocol:
              description:
                - Protocol above IP layer.
              type: str
              sample: tcp
            tcp_flags:
              description:
                - TCP flags or logical combination of flags.
              type: str
              sample:
            action:
              description:
                - Action if rule matches.
                - V(accept) or V(discard).
              type: str
              sample: accept
              choices:
                - accept
                - discard
N)AnsibleModulemissing_required_lib)ROBOT_DEFAULT_ARGUMENT_SPECBASE_URLfetch_url_jsonfetch_url_json_with_retriesCheckDoneTimeoutException)	urlencode)	to_nativeto_textTF	name
ip_versiondst_ipdst_portsrc_ipsrc_portprotocol	tcp_flagsactioninputoutputc                 ^    t               }| j                         D ]  \  }}||v s|||<    |S N)dictitems)
dictionaryfieldsresultkvs        m/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/hrobot/plugins/modules/firewall.pyrestrict_dictr&     s<    VF  " 1;F1I M    c                     t        | g d      }t               |d<   t        D ]<  }| d   j                  |      xs g D cg c]  }t        |t               c}|d   |<   > |S c c}w )N)portstatusfilter_ipv6whitelist_hosrules)r&   r   RULESgetRULE_OPTION_NAMES)configr"   rulesetrules       r%   restrict_firewall_configr4     st    6#UVFfF7O 
 w++G4:$
 $ 12$
w 

 M	$
s   A#c                 d    | j                  |      }|||<   d}||xs |   }|||k7  }|r|||<   |S )NF)r/   )beforeafterparamsr   
param_namebvchangedpvs           r%   updater=     sL    	D	BE$KG	
"d	#B	~(E$KNr'   c                     | || S d| v r| j                  d      \  } }n| d}} t        t        j                  t	        |             j
                        }|dk(  r|j                         dk(  rdnd}|dz   |z   S )N/ ipv432128)splitr   	ipaddress
ip_addressr   
compressedlower)ipr   rangeip_addrs       r%   normalize_iprL     s~    	zZ'	
byHHSM	EE	,,WR[9DDEG{"((*f4%S=5  r'   c                    | d   |   }|d   |   }|d   |   }t        |      t        |      k7  }t        |      D ]  \  }}	t        |	d   |	d         |	d<   t        |	d   |	d         |	d<   |t        |      k  r6||   }
t        |
d   |
d         |
d<   t        |
d   |
d         |
d<   |
|	k7  rd}|j                  |	        |S )Nr-   r   r   r   T)len	enumeraterL   append)r6   r7   r8   r2   before_rulesafter_rulesparams_rulesr;   nor3   before_rules              r%   update_rulesrV     s    '?7+L.)K'?7+L,3|#44Gl+ 	!D%d8nd<6HIX%d8nd<6HIXL!!&r*K$0X1FT`Ha$bK!$0X1FT`Ha$bK!d"4 	! Nr'   c           	          t        |d   |         D ]6  \  }}|j                         D ]  \  }}|	|| dj                  |||      <     8 y )Nr-   zrules[{0}][{1}][{2}])rO   r   format)r   rulenamer   ir3   r#   r$   s          r%   encode_ruler[     s_    U7^H56 J4JJL 	JDAq}HI-44Xq!DE	JJr'   c                  :    t               } t        D ]  }g | |<   	 | S r   )r   r.   )r-   r2   s     r%   create_default_rules_objectr]     s&    FE gLr'   c                 P    | j                         } | j                  dd      | d<   | S )Nr,   Fallowlist_hos)copyr/   )firewall_results    r%   
fix_namingrb     s-    %**,O'6':':?E'ROO$r'   c                     | d   d   dk7  S )Nfirewallr*   
in process )r"   errors     r%   firewall_configuredrh   !  s    *h'<77r'   c            "         t        t        d      t        d      t        ddddg      t        d      t        dddd	g      t        dd
g      t        dt        t        ddt        t        d      t        d      t        d      t        d      t        d      t        d      t        d      t        d      t        ddddg      	      t        dgdgdg            t        ddt        t        d      t        d      t        d      t        d      t        d      t        d      t        d      t        d      t        ddddg      	      t        dgdgdg                        t        dd      t        dd      t        dd      t        dd            } | j                  t               t        | d      }t        s |j                  t        d      t               |j                  d    dk(  rd!nd"|j                  d#<   |j                  d$   i |j                  d$<   t        D ]3  }|j                  d$   j                  |      "g |j                  d$   |<   5 |j                  d%   xs |j                  d&   }d'j                  t        |      }|j                  d(   r3	 t        ||t        |j                  d)   |j                  d*   +      \  }}n-t!        ||      \  }}t        ||      s|j                  d.-       d/   }|j                  d$      st#               |d$<   t%        |      }	t        |	      }
d0}|t        |	|
|j                  d1      z  }|t        |	|
|j                  d2      z  }|t        |	|
|j                  d#      z  }|t        |	|
|j                  d
d3      z  }t#               |
d$<   |j                  d#   d!k(  r&t        D ]  }|t'        |	|
|j                  |      z  } d}d }|r=|j(                  s0d'j                  t        |      }d4d5i}t        |
      }t+        |d1         j-                         |d1<   t+        |d
         j-                         |d
<   |d$= t        D ]  }t/        |||
        t!        ||d6|j                  d7   t1        |      |8      \  }}|j                  d(   r?t        ||      s3	 t        ||t        |j                  d)   |j                  d*   d9      \  }}|d/   }|j                  d$      st#               |d$<   |d#   }|d:k7  rt%        |      }
d0}|r^t        |      }|
j9                         D ]  \  }}|d$k7  s|
|   ||<    |||d#<   t               |d$<   t        D ]  }|
d$   |   |d$   |<    |j;                  |t        t=        |	      t=        |
      ;      t=              <       y # t        $ r}|j                  d,-       Y d }~d }~ww xY w# t        $ r4}|j2                  |j4                  }}|j7                  d,       Y d }~6d }~ww xY w)=Nstr)typeintmainkvm)rk   defaultchoicesboolpresentabsentr,   )rk   aliasesr   listTacceptdiscard)rk   requiredrp   r   r   )r   r   r   )rk   elementsoptionsrequired_by)r   r   )rk   rz      )rk   ro   
      )	server_ipserver_numberr)   r+   stater_   r-   update_timeoutwait_for_configured
wait_delaytimeout)argument_specsupports_check_moderE   )msg	exceptionr   activedisabledr*   r-   r   r   z{0}/firewall/{1}r   r   r   )check_done_callbackcheck_done_delaycheck_done_timeoutz4Timeout while waiting for firewall to be configured.)r   z>Firewall configuration cannot be read as it is not configured.rd   Fr+   r)   r_   zContent-typez!application/x-www-form-urlencodedPOSTr   )methodr   dataheaders)r   r   r   
skip_firstre   )r6   r7   )r;   diffrd   )r   r=   r   r   HAS_IPADDRESS	fail_jsonr   IPADDRESS_IMP_ERRr8   r.   r/   rX   r	   r   rh   r   r
   r]   r4   rV   
check_moderj   rH   r[   r   r"   rg   warnr   	exit_jsonrb   )r   modulechain	server_idurlr"   rg   dummyfull_beforer6   r7   r;   r2   construct_resultconstruct_statusr   r   e
full_afterr#   r$   s                        r%   rm   rm   %  sL   E"&ufvuoFf%y9h:OP0ABFVTu%U+'5)'5)5)E*)?TU
>  ~|nXdWef
h Vfdu%U+'5)'5)5)E*)?TU
?  ~|nXdWef
h)
 0 3 fd;UB/%-E#MH 45# F
 1+>J[\ ,2==+AY+NhU_FMM(}}W%!#g /==!%%e,4,.FMM'"5)/ k*LfmmO.LI 
#
#Hi
8C}}*+		Y7$7!'|!<#)==#;MFE 'vs3"651!ab$K??7#:<G%k2F LEGvfeV]]MBBGvfeV]]F;;GvfeV]]H==GvfeV]]O_UUG02E'N}}X(* 	KG|FE6=='JJG	K v(( '')<!#FGE{!$}"56<<>] #D$9 : @ @ B_M 	.Ggu-	.&MM"234
 ==./8KFTY8ZT ;(;%+]]<%@'-}}Y'?#! J'
~~g&"="?Jw%h/|+,Z8E$+&
KKM 	)DAqG| %a
1	) '#3Jx "f
7 	CG+0>'+BJw(	C f%U#
 J'  c ) 	Y!WXX	Yn - T !!''RSSTs0   1W- &2X -	X6XX	Y)YY__main__r   )+
__future__r   r   r   rk   __metaclass__DOCUMENTATIONEXAMPLESRETURN	tracebackansible.module_utils.basicr   r   ?ansible_collections.community.hrobot.plugins.module_utils.robotr   r	   r
   r   r   +ansible.module_utils.six.moves.urllib.parser   +ansible.module_utils.common.text.convertersr   r   rE   r   r   ImportErrorexc
format_excr0   r.   r&   r4   r=   rL   rV   r[   r]   rb   rh   rm   __name__rf   r'   r%   <module>r      s    A @Qf*Xd
L  J  B JM 
 
(	
!$J8[| zF C  ,	,,.Ms   A9 9B>BB