Skip to content

Commit

Permalink
See #62
Browse files Browse the repository at this point in the history
  • Loading branch information
trickert76 committed Aug 4, 2021
1 parent ab7eb85 commit 0d5e84b
Show file tree
Hide file tree
Showing 9 changed files with 1,152 additions and 0 deletions.
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,11 @@ pve_groups: [] # List of group definitions to manage in PVE. See section on User
pve_users: [] # List of user definitions to manage in PVE. See section on User Management.
pve_storages: [] # List of storages to manage in PVE. See section on Storage Management.
pve_datacenter_cfg: {} # Dictionary to configure the PVE datacenter.cfg config file.
pve_manage_firewall: false # manage the proxmox firewall by configuring aliases, ipsets, security groups and assignments
pve_firewall_cluster_enabled: true # enable cluster firewall on success
pve_firewall_aliases: [] # List of dict of aliases with keys name, comment and cidr, which can be either IPv4 or IPv6
pve_firewall_ipsets: [] # List of dict of ip sets with keys name, comment and aliases, which is a list of aliases
pve_firewall_groups: [] # List of dict of a security group. It contains a list rules and which hosts should be assigned
```

To enable clustering with this role, configure the following variables appropriately:
Expand Down Expand Up @@ -539,6 +544,88 @@ pve_acls:
Refer to `library/proxmox_role.py` [link][user-module] and
`library/proxmox_acl.py` [link][acl-module] for module documentation.

## Firewall management

This feature is not fully implemented yet.

A list of aliases.

```
pve_firewall_aliases:
- name: host1-ipv4
cidr: 10.0.0.1
comment: my_host
- name: host1-ipv6
cidr: fd01:1::1
comment: my_host
- name: host2-ipv4
cidr: 10.0.0.2
comment: my_host
- name: host2-ipv6
cidr: fd01:1::2
comment: my_host
```

A list of ip sets

```
pve_firewall_ipsets:
- name: "all-nodes"
comment: "all nodes"
aliases:
- host1-ipv4
- host1-ipv6
- host2-ipv4
- host2-ipv6
- name: "ipv4-nodes"
comment: "all nodes with ipv4 addresses"
aliases:
- host1-ipv4
- host2-ipv4
- name: "ipv6-nodes"
comment: "all nodes with ipv6 addresses"
aliases:
- host1-ipv6
- host2-ipv6
```

A list of security groups

```
pve_firewall_groups:
- name: "proxmox-hosts"
comment: "rules for all proxmox hosts"
assign_hosts: "{{ groups[proxmox_firewall_group] }}"
assign_cluster: true
rules:
- comment: "allow internal communication between all proxmox nodes"
source: "+all-nodes"
dest: "+all-nodes"
- action: "ACCEPT"
type: "in"
enable: true
dest: "+all-nodes"
proto: tcp
dport: "{{ pve_ssh_port }}"
comment: "allow all to access SSH"
log: "nolog"
- action: "ACCEPT"
type: "in"
enable: true
dest: "+all-nodes"
proto: tcp
dport: 8006
comment: "allow all to access proxmox WebUI"
log: "nolog"
- action: "ACCEPT"
type: "in"
enable: true
dest: "+all-nodes"
macro: "HTTP"
comment: "allow Letsencrypt to access Proxmox Nodes"
log: "nolog"
```

## Storage Management

You can use this role to manage storage within Proxmox VE (both in
Expand Down
54 changes: 54 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,57 @@ pve_acls: []
pve_storages: []
pve_ssh_port: 22
pve_manage_ssh: true

# enable to configure the database too
pve_manage_firewall: false
pve_firewall_cluster_enabled: true
pve_firewall_group: "{{ pve_group }}"

# pve_firewall_aliases:
# - name: xyz
# cidr: 1.2.3.4
# comment: my_host

pve_firewall_ipsets:
- name: "network-nodes"
comment: "all nodes"
aliases: []
- name: "proxmox-nodes"
comment: "all proxmox hosts"
group: "{{ pve_group }}"
pve_firewall_groups:
- name: "proxmox-hosts"
comment: "rules for all proxmox hosts"
assign_hosts: "{{ groups[proxmox_firewall_group] }}"
assign_cluster: true
rules:
- action: "ACCEPT"
type: "in"
enable: true
source: "+proxmox-nodes"
dest: "+proxmox-nodes"
comment: "allow internal communication between all proxmox nodes"
log: "nolog"
- action: "ACCEPT"
type: "in"
enable: true
dest: "+proxmox-nodes"
proto: tcp
dport: "{{ pve_ssh_port }}"
comment: "allow all to access SSH"
log: "nolog"
- action: "ACCEPT"
type: "in"
enable: true
dest: "+proxmox-nodes"
proto: tcp
dport: 8006
comment: "allow all to access proxmox WebUI"
log: "nolog"
- action: "ACCEPT"
type: "in"
enable: true
dest: "+proxmox-nodes"
macro: "HTTP"
comment: "allow Letsencrypt to access Proxmox Nodes"
log: "nolog"
190 changes: 190 additions & 0 deletions library/proxmox_firewall_alias.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

ANSIBLE_METADATA = {
'metadata_version': '1.0',
'status': ['stableinterface'],
'supported_by': 'trickert76'
}

DOCUMENTATION = '''
---
module: proxmox_firewall_alias
short_description: Manages firewall aliases in Proxmox
options:
name:
required: true
description:
- Name of the alias.
cidr:
required: true
description:
- CIDR of the alias.
state:
required: false
default: "present"
choices: [ "present", "absent" ]
description:
- Specifies whether the alias should exist or not.
comment:
required: false
description:
- Optionally sets the alias's comment in PVE.
author:
- Thoralf Rickert-Wendt (@trickert76)
'''

EXAMPLES = '''
- name: Create alias for a host
proxmox_firewall_alias:
name: myhost
cidr: 127.0.0.1
- name: Create special host
proxmox_firewall_alias:
name: gateway
cidr: fd80::1
comment: Our gateway.
'''

RETURN = '''
'''

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_text
from ansible.module_utils.pvesh import ProxmoxShellError
import ansible.module_utils.pvesh as pvesh

class ProxmoxFirewallAlias(object):
def __init__(self, module, result):
self.module = module
self.result = result
self.name = module.params['name']
self.cidr = module.params['cidr']
self.state = module.params['state']
self.comment = module.params['comment']

def lookup(self):
try:
return pvesh.get("cluster/firewall/aliases/{}".format(self.name))
except ProxmoxShellError as e:
if e.status_code == 400:
return None
self.module.fail_json(msg=e.message, status_code=e.status_code, **self.result)

def remove_alias(self):
try:
pvesh.delete("cluster/firewall/aliases/{}".format(self.name))
return (True, None)
except ProxmoxShellError as e:
return (False, e.message)

def create_alias(self):
new_alias = {}
new_alias['name'] = self.name
new_alias['cidr'] = self.cidr
if self.comment is not None:
new_alias['comment'] = self.comment

try:
pvesh.create("cluster/firewall/aliases", **new_alias)
return (True, None)
except ProxmoxShellError as e:
return (False, e.message)

def modify_alias(self):
existing_alias = self.lookup()
modified_alias = {}
modified_alias['cidr'] = self.cidr
if self.comment is not None:
modified_alias['comment'] = self.comment

updated_fields = []
error = None

for key in modified_alias:
staged_value = modified_alias.get(key)
if key not in existing_alias or staged_value != existing_alias.get(key):
updated_fields.append(key)

if self.module.check_mode:
self.module.exit_json(changed=bool(updated_fields), expected_changes=updated_fields)

if not updated_fields:
# No changes necessary
return (updated_fields, error)

try:
pvesh.set("cluster/firewall/aliases/{}".format(self.name), **modified_alias)
except ProxmoxShellError as e:
error = e.message

return (updated_fields, error)

def main():
# Refer to https://pve.proxmox.com/pve-docs/api-viewer/index.html
module = AnsibleModule(
argument_spec = dict(
name=dict(type='str', required=True),
cidr=dict(type='str', required=True),
state=dict(default='present', choices=['present', 'absent'], type='str'),
comment=dict(default=None, type='str'),
),
supports_check_mode=True
)

result = {}
pve = ProxmoxFirewallAlias(module, result)

before_alias = pve.lookup()

changed = False
error = None
result['name'] = pve.name
result['state'] = pve.state

if pve.state == 'absent':
if before_alias is not None:
if module.check_mode:
module.exit_json(changed=True)

(changed, error) = pve.remove_alias()

if error is not None:
module.fail_json(name=pve.name, msg=error)
elif pve.state == 'present':
if not before_alias:
if module.check_mode:
module.exit_json(changed=True)

(changed, error) = pve.create_alias()
else:
# modify alias (note: this function is check mode aware)
(updated_fields, error) = pve.modify_alias()

if updated_fields:
changed = True
result['updated_fields'] = updated_fields

if error is not None:
module.fail_json(name=pve.name, msg=error)

result['changed'] = changed

after_alias = pve.lookup()
if after_alias is not None:
result['alias'] = after_alias

if module._diff:
if before_alias is None:
before_alias = ''
if after_alias is None:
after_alias = ''
result['diff'] = dict(before=before_alias, after=after_alias)

module.exit_json(**result)

if __name__ == '__main__':
main()
Loading

0 comments on commit 0d5e84b

Please sign in to comment.