PyPANRestV2 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.1
2
+ Name: PyPANRestV2
3
+ Version: 2.1.0
4
+ Summary: Python tools for interacting with Palo Alto Networks REST API.
5
+ License: MIT
6
+ Author: Mark Rzepa
7
+ Author-email: mark@rzepa.com
8
+ Requires-Python: >=3.11
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: dnspython (>=2.6.1)
14
+ Requires-Dist: icecream (>=2.1.3)
15
+ Requires-Dist: pycountry (>=23.12.11)
16
+ Requires-Dist: python-dotenv (>=1.0.1)
17
+ Requires-Dist: requests (>=2.31.0)
18
+ Requires-Dist: tqdm (>=4.66.2)
19
+ Requires-Dist: validators (>=0.22.0)
20
+ Requires-Dist: xmltodict (>=0.13.0)
21
+ Description-Content-Type: text/markdown
22
+
23
+ # PyPanRestV2
24
+
25
+ **PyPanRestV2** is a Python library designed to simplify interactions with Palo Alto Networks firewalls and Panorama via their REST API. It provides a higher level of abstraction, allowing users to manage firewalls and Panorama without needing to construct REST requests manually or work with XML for areas of the firewall configuration that still require it.
26
+
27
+ ---
28
+
29
+ ## Features
30
+
31
+ - **High-Level Abstraction**: Simplifies interaction with the Palo Alto Networks API.
32
+ - **Support for Firewalls and Panorama**: Manage both individual firewalls and Panorama devices.
33
+ - **REST API Integration**: Allows seamless communication with devices.
34
+ - **Convenient Pythonic Objects**: Intuitive Python objects for interacting with specific sections of Palo Alto firewall configurations.
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ To install `PyPanRestV2`, clone the repository and install it as a package:
41
+
42
+ ```bash
43
+ # Clone the repository
44
+ git clone https://github.com/mrzepa/pypanrestv2.git
45
+
46
+ # Navigate to the project directory
47
+ cd pypanrestv2
48
+
49
+ # Install the package in development mode
50
+ pip install -e .
51
+ ```
52
+
53
+ This will install the package and all required dependencies automatically. The `-e` flag installs the package in "editable" mode, which is useful if you plan to modify the code or contribute to the project.
54
+
55
+ ---
56
+
57
+ ## Basic Usage
58
+
59
+ ### Import the Required Classes
60
+ Start by importing the necessary classes from the library:
61
+
62
+ ```python
63
+ from pypantrestv2 import Firewall, Panorama
64
+ ```
65
+
66
+ ### Connect to a Firewall or Panorama Device
67
+ Create a `Firewall` or `Panorama` object by providing the required connection details:
68
+
69
+ For a **Firewall**:
70
+ ```python
71
+ firewall = Firewall(base_url="192.168.1.1", api_key="12345")
72
+ ```
73
+
74
+ For **Panorama**:
75
+ ```python
76
+ panorama = Panorama(base_url="192.168.2.1", username="admin", password="my_password")
77
+ ```
78
+
79
+ ### Common Use Cases
80
+
81
+ #### 1. Managing Security Rules
82
+ ```python
83
+ from pypanrestv2.Policies import SecurityRules
84
+
85
+ # Create a new security rule
86
+ security_rule = SecurityRules(firewall, name='allow_web')
87
+ security_rule.source_zone = ['trust']
88
+ security_rule.destination_zone = ['untrust']
89
+ security_rule.source = ['any']
90
+ security_rule.destination = ['any']
91
+ security_rule.application = ['web-browsing']
92
+ security_rule.service = ['application-default']
93
+ security_rule.action = 'allow'
94
+ security_rule.create()
95
+
96
+ # Modify an existing rule
97
+ existing_rule = SecurityRules(firewall, name='existing_rule')
98
+ existing_rule.refresh() # Load current configuration
99
+ existing_rule.action = 'deny'
100
+ existing_rule.update()
101
+ ```
102
+
103
+ #### 2. Managing Address Objects
104
+ ```python
105
+ from pypanrestv2.Objects import Addresses
106
+
107
+ # Create a new address object
108
+ address = Addresses(firewall, name='web_server')
109
+ address.value = '192.168.1.100'
110
+ address.type = 'ip-netmask'
111
+ address.create()
112
+
113
+ # Get all address objects
114
+ all_addresses = Addresses.get_all(firewall)
115
+ ```
116
+
117
+ #### 3. Working with Panorama Device Groups
118
+ ```python
119
+ from pypanrestv2 import Panorama
120
+
121
+ # Initialize Panorama connection
122
+ panorama = Panorama(base_url='panorama.example.com', api_key='YOUR_API_KEY')
123
+
124
+ # Add a device to a device group
125
+ device_group = panorama.device_groups.get('Branch_Offices')
126
+ device_group.add_device('serial123')
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Repository
132
+
133
+ Visit the project's GitHub repository for source code, documentation, enhancements, and contributions:
134
+
135
+ [PyPanRestV2 Repository on GitHub](https://github.com/wellhealthtechnologies/PyPanRestV2.git)
136
+
137
+ ---
138
+
139
+ ## Requirements
140
+
141
+ - **Python 3.11+** (or higher)
142
+ - **Palo Alto Devices** or Panorama
143
+ - Python modules listed in requirements.txt
144
+
145
+ ---
146
+
147
+ ## API Documentation
148
+
149
+ The SDK provides access to the following main components:
150
+
151
+ ### Core Modules
152
+ - `Firewall/Panorama`: Base connection and authentication
153
+ - `Policies`: Security rules, NAT rules, and policy management
154
+ - `Objects`: Address objects, service objects, and security profiles
155
+ - `Network`: Interfaces, zones, and routing configuration
156
+ - `Device`: System settings and device management
157
+
158
+ ### Error Handling
159
+
160
+ The SDK uses custom exceptions for better error handling:
161
+
162
+ ```python
163
+ from pypanrestv2.Exceptions import PANConnectionError, PANConfigError
164
+
165
+ try:
166
+ firewall = Firewall(base_url='192.168.1.1', api_key='invalid_key')
167
+ firewall.test_connection()
168
+ except PANConnectionError as e:
169
+ print(f'Connection failed: {e}')
170
+ except PANConfigError as e:
171
+ print(f'Configuration error: {e}')
172
+ ```
173
+
174
+ Common errors and solutions:
175
+ - `PANConnectionError`: Check network connectivity and API credentials
176
+ - `PANConfigError`: Verify object names and configuration values
177
+ - `PANNotFoundError`: Ensure referenced objects exist
178
+
179
+ ## Status and Updates
180
+
181
+ This SDK is actively maintained and regularly updated to support new PAN-OS versions. While not all API endpoints are implemented, core functionality is stable and production-ready. Check the GitHub repository for the latest updates and supported features.
182
+
183
+ ## Contributing
184
+
185
+ Contributions are welcome! If you want to report issues, request features, or contribute to the library:
186
+
187
+ 1. Fork the repository.
188
+ 2. Create a feature branch: `git checkout -b my-feature`.
189
+ 3. Commit your changes: `git commit -m "Add detailed description of changes"`.
190
+ 4. Push to the branch: `git push origin my-feature`.
191
+ 5. Submit a pull request.
192
+
193
+ Be sure to check the documentation, if provided, before starting contributions.
194
+
195
+ ---
196
+
197
+ ## License
198
+
199
+ This project is licensed under the MIT. See the [LICENSE](./https://opensource.org/license/mit) file for details.
200
+
201
+ ---
202
+
203
+ ## Author
204
+
205
+ Mark Rzepa
206
+ mark@rzepa.com
207
+
208
+ ---
209
+
@@ -0,0 +1,186 @@
1
+ # PyPanRestV2
2
+
3
+ **PyPanRestV2** is a Python library designed to simplify interactions with Palo Alto Networks firewalls and Panorama via their REST API. It provides a higher level of abstraction, allowing users to manage firewalls and Panorama without needing to construct REST requests manually or work with XML for areas of the firewall configuration that still require it.
4
+
5
+ ---
6
+
7
+ ## Features
8
+
9
+ - **High-Level Abstraction**: Simplifies interaction with the Palo Alto Networks API.
10
+ - **Support for Firewalls and Panorama**: Manage both individual firewalls and Panorama devices.
11
+ - **REST API Integration**: Allows seamless communication with devices.
12
+ - **Convenient Pythonic Objects**: Intuitive Python objects for interacting with specific sections of Palo Alto firewall configurations.
13
+
14
+ ---
15
+
16
+ ## Installation
17
+
18
+ To install `PyPanRestV2`, clone the repository and install it as a package:
19
+
20
+ ```bash
21
+ # Clone the repository
22
+ git clone https://github.com/mrzepa/pypanrestv2.git
23
+
24
+ # Navigate to the project directory
25
+ cd pypanrestv2
26
+
27
+ # Install the package in development mode
28
+ pip install -e .
29
+ ```
30
+
31
+ This will install the package and all required dependencies automatically. The `-e` flag installs the package in "editable" mode, which is useful if you plan to modify the code or contribute to the project.
32
+
33
+ ---
34
+
35
+ ## Basic Usage
36
+
37
+ ### Import the Required Classes
38
+ Start by importing the necessary classes from the library:
39
+
40
+ ```python
41
+ from pypantrestv2 import Firewall, Panorama
42
+ ```
43
+
44
+ ### Connect to a Firewall or Panorama Device
45
+ Create a `Firewall` or `Panorama` object by providing the required connection details:
46
+
47
+ For a **Firewall**:
48
+ ```python
49
+ firewall = Firewall(base_url="192.168.1.1", api_key="12345")
50
+ ```
51
+
52
+ For **Panorama**:
53
+ ```python
54
+ panorama = Panorama(base_url="192.168.2.1", username="admin", password="my_password")
55
+ ```
56
+
57
+ ### Common Use Cases
58
+
59
+ #### 1. Managing Security Rules
60
+ ```python
61
+ from pypanrestv2.Policies import SecurityRules
62
+
63
+ # Create a new security rule
64
+ security_rule = SecurityRules(firewall, name='allow_web')
65
+ security_rule.source_zone = ['trust']
66
+ security_rule.destination_zone = ['untrust']
67
+ security_rule.source = ['any']
68
+ security_rule.destination = ['any']
69
+ security_rule.application = ['web-browsing']
70
+ security_rule.service = ['application-default']
71
+ security_rule.action = 'allow'
72
+ security_rule.create()
73
+
74
+ # Modify an existing rule
75
+ existing_rule = SecurityRules(firewall, name='existing_rule')
76
+ existing_rule.refresh() # Load current configuration
77
+ existing_rule.action = 'deny'
78
+ existing_rule.update()
79
+ ```
80
+
81
+ #### 2. Managing Address Objects
82
+ ```python
83
+ from pypanrestv2.Objects import Addresses
84
+
85
+ # Create a new address object
86
+ address = Addresses(firewall, name='web_server')
87
+ address.value = '192.168.1.100'
88
+ address.type = 'ip-netmask'
89
+ address.create()
90
+
91
+ # Get all address objects
92
+ all_addresses = Addresses.get_all(firewall)
93
+ ```
94
+
95
+ #### 3. Working with Panorama Device Groups
96
+ ```python
97
+ from pypanrestv2 import Panorama
98
+
99
+ # Initialize Panorama connection
100
+ panorama = Panorama(base_url='panorama.example.com', api_key='YOUR_API_KEY')
101
+
102
+ # Add a device to a device group
103
+ device_group = panorama.device_groups.get('Branch_Offices')
104
+ device_group.add_device('serial123')
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Repository
110
+
111
+ Visit the project's GitHub repository for source code, documentation, enhancements, and contributions:
112
+
113
+ [PyPanRestV2 Repository on GitHub](https://github.com/wellhealthtechnologies/PyPanRestV2.git)
114
+
115
+ ---
116
+
117
+ ## Requirements
118
+
119
+ - **Python 3.11+** (or higher)
120
+ - **Palo Alto Devices** or Panorama
121
+ - Python modules listed in requirements.txt
122
+
123
+ ---
124
+
125
+ ## API Documentation
126
+
127
+ The SDK provides access to the following main components:
128
+
129
+ ### Core Modules
130
+ - `Firewall/Panorama`: Base connection and authentication
131
+ - `Policies`: Security rules, NAT rules, and policy management
132
+ - `Objects`: Address objects, service objects, and security profiles
133
+ - `Network`: Interfaces, zones, and routing configuration
134
+ - `Device`: System settings and device management
135
+
136
+ ### Error Handling
137
+
138
+ The SDK uses custom exceptions for better error handling:
139
+
140
+ ```python
141
+ from pypanrestv2.Exceptions import PANConnectionError, PANConfigError
142
+
143
+ try:
144
+ firewall = Firewall(base_url='192.168.1.1', api_key='invalid_key')
145
+ firewall.test_connection()
146
+ except PANConnectionError as e:
147
+ print(f'Connection failed: {e}')
148
+ except PANConfigError as e:
149
+ print(f'Configuration error: {e}')
150
+ ```
151
+
152
+ Common errors and solutions:
153
+ - `PANConnectionError`: Check network connectivity and API credentials
154
+ - `PANConfigError`: Verify object names and configuration values
155
+ - `PANNotFoundError`: Ensure referenced objects exist
156
+
157
+ ## Status and Updates
158
+
159
+ This SDK is actively maintained and regularly updated to support new PAN-OS versions. While not all API endpoints are implemented, core functionality is stable and production-ready. Check the GitHub repository for the latest updates and supported features.
160
+
161
+ ## Contributing
162
+
163
+ Contributions are welcome! If you want to report issues, request features, or contribute to the library:
164
+
165
+ 1. Fork the repository.
166
+ 2. Create a feature branch: `git checkout -b my-feature`.
167
+ 3. Commit your changes: `git commit -m "Add detailed description of changes"`.
168
+ 4. Push to the branch: `git push origin my-feature`.
169
+ 5. Submit a pull request.
170
+
171
+ Be sure to check the documentation, if provided, before starting contributions.
172
+
173
+ ---
174
+
175
+ ## License
176
+
177
+ This project is licensed under the MIT. See the [LICENSE](./https://opensource.org/license/mit) file for details.
178
+
179
+ ---
180
+
181
+ ## Author
182
+
183
+ Mark Rzepa
184
+ mark@rzepa.com
185
+
186
+ ---
@@ -0,0 +1,220 @@
1
+ class Operator:
2
+ def __init__(self, context):
3
+ self.context = context
4
+
5
+ @property
6
+ def context(self) -> str:
7
+ return self._context
8
+
9
+ @context.setter
10
+ def context(self, value):
11
+ if not isinstance(value, str):
12
+ raise ValueError("Context must be a string.")
13
+
14
+
15
+ class PatternMatchOperator(Operator):
16
+ def __init__(self, context, pattern, qualifier):
17
+ super().__init__(context)
18
+ self.pattern = pattern
19
+ self.qualifier = self.initialize_qualifier(qualifier)
20
+
21
+ def initialize_qualifier(self, qualifier):
22
+ if 'entry' in qualifier:
23
+ return [{entry['@name']: entry['value']} for entry in qualifier['entry']]
24
+ return []
25
+
26
+ def to_dict(self):
27
+ return {
28
+ 'type': 'pattern-match',
29
+ 'context': self.context,
30
+ 'pattern': self.pattern,
31
+ 'qualifier': {'entry': self.qualifier},
32
+ }
33
+
34
+
35
+ class GreaterThanOperator(Operator):
36
+ def __init__(self, context, value, qualifier):
37
+ super().__init__(context)
38
+ self.value = value
39
+ self.validate_value(value)
40
+ self.qualifier = self.initialize_qualifier(qualifier)
41
+
42
+ def validate_value(self, value):
43
+ if not isinstance(value, int) or not (0 <= value <= 4294967295):
44
+ raise ValueError("Value must be an integer between 0 and 4294967295.")
45
+
46
+ def initialize_qualifier(self, qualifier):
47
+ if 'entry' in qualifier:
48
+ return [{entry['@name']: entry['value']} for entry in qualifier['entry']]
49
+ return []
50
+
51
+ def to_dict(self):
52
+ return {
53
+ 'type': 'greater-than',
54
+ 'context': self.context,
55
+ 'value': self.value,
56
+ 'qualifier': {'entry': self.qualifier},
57
+ }
58
+
59
+
60
+ class LessThanOperator(Operator):
61
+ def __init__(self, context, value, qualifier):
62
+ super().__init__(context)
63
+ self.value = value
64
+ self.validate_value(value)
65
+ self.qualifier = self.initialize_qualifier(qualifier)
66
+
67
+ def validate_value(self, value):
68
+ if not isinstance(value, int) or not (0 <= value <= 4294967295):
69
+ raise ValueError("Value must be an integer between 0 and 4294967295.")
70
+
71
+ def initialize_qualifier(self, qualifier):
72
+ if 'entry' in qualifier:
73
+ return [{entry['@name']: entry['value']} for entry in qualifier['entry']]
74
+ return []
75
+
76
+ def to_dict(self):
77
+ return {
78
+ 'type': 'less-than',
79
+ 'context': self.context,
80
+ 'value': self.value,
81
+ 'qualifier': {'entry': self.qualifier},
82
+ }
83
+
84
+
85
+ class EqualToOperator(Operator):
86
+ def __init__(self, context, position, mask, value):
87
+ super().__init__(context)
88
+ self.position = position
89
+ self.mask = mask
90
+ self.value = value
91
+ self.validate_position(position)
92
+ self.validate_mask(mask)
93
+ self.validate_value(value)
94
+
95
+ def validate_position(self, position):
96
+ if not isinstance(position, str) or len(position) > 127:
97
+ raise ValueError("Position must be a string up to 127 characters long.")
98
+
99
+ def validate_mask(self, mask):
100
+ if not isinstance(mask, str) or not re.match(r'^0[xX][0-9A-Fa-f]{8}$', mask):
101
+ raise ValueError("Mask must be a 4-byte hex value.")
102
+
103
+ def validate_value(self, value):
104
+ if not isinstance(value, str) or len(value) > 10:
105
+ raise ValueError("Value must be a string up to 10 characters long.")
106
+
107
+ def to_dict(self):
108
+ return {
109
+ 'type': 'equal-to',
110
+ 'context': self.context,
111
+ 'position': self.position,
112
+ 'mask': self.mask,
113
+ 'value': self.value,
114
+ }
115
+
116
+ class OrConditionEntry:
117
+ def __init__(self, name, operator_data):
118
+ self.validate_name(name)
119
+ self.name = name
120
+ self.operator = self.initialize_operator(operator_data)
121
+
122
+ def validate_name(self, name):
123
+ if not isinstance(name, str) or len(name) > 31:
124
+ raise ValueError("Invalid name in or-condition entry.")
125
+
126
+ def initialize_operator(self, operator_data):
127
+ if not isinstance(operator_data, dict):
128
+ raise ValueError("Operator data must be a dictionary.")
129
+
130
+ operator_type = operator_data.get('type')
131
+ if operator_type == 'pattern-match':
132
+ return PatternMatchOperator(context=operator_data.get('context'),
133
+ pattern=operator_data.get('pattern'),
134
+ qualifier=operator_data.get('qualifier'))
135
+ elif operator_type == 'greater-than':
136
+ return GreaterThanOperator(context=operator_data.get('context'),
137
+ value=operator_data.get('value'),
138
+ qualifier=operator_data.get('qualifier'))
139
+ elif operator_type == 'less-than':
140
+ return GreaterThanOperator(context=operator_data.get('context'),
141
+ value=operator_data.get('value'),
142
+ qualifier=operator_data.get('qualifier'))
143
+ elif operator_type == 'equal-to':
144
+ return EqualToOperator(context=operator_data.get('context'),
145
+ position=operator_data.get('position'),
146
+ mask=operator_data.get('mask'),
147
+ value=operator_data.get('value'))
148
+ else:
149
+ raise ValueError(f"Unsupported operator type: {operator_type}")
150
+
151
+ def to_dict(self):
152
+ # Convert the OrConditionEntry instance to a dictionary format
153
+ or_condition_dict = {
154
+ 'name': self.name,
155
+ # Include operator serialization here once operator.to_dict() is defined
156
+ 'operator': self.operator.to_dict() if self.operator else None,
157
+ }
158
+ return or_condition_dict
159
+
160
+ class AndConditionEntry:
161
+ def __init__(self, name, or_condition=None):
162
+ self.validate_name(name)
163
+ self.name = name
164
+ self.or_condition = self.initialize_or_condition(or_condition)
165
+
166
+ def validate_name(self, name):
167
+ if not isinstance(name, str) or len(name) > 31:
168
+ raise ValueError("Invalid name in and-condition entry.")
169
+ # Ensures the name consists only of allowed characters
170
+ if not all(char.isalnum() or char in "._-" for char in name):
171
+ raise ValueError("Name in and-condition entry contains invalid characters.")
172
+
173
+ def initialize_or_condition(self, or_condition):
174
+ if or_condition and 'entry' in or_condition:
175
+ return [OrConditionEntry(**entry) for entry in or_condition['entry']]
176
+ return []
177
+
178
+ def to_dict(self):
179
+ # Convert the AndConditionEntry instance to a dictionary format
180
+ and_condition_dict = {
181
+ 'name': self.name,
182
+ # Serialize or-condition if it's present
183
+ 'or-condition': {'entry': [condition.to_dict() for condition in self.or_condition] if self.or_condition else None},
184
+ }
185
+ return and_condition_dict
186
+
187
+
188
+ class SignatureEntry:
189
+ def __init__(self, name, comment="", scope="protocol-data-unit", order_free="no", and_condition=None):
190
+ self.validate_name(name)
191
+ self.name = name
192
+ self.comment = comment
193
+ self.scope = self.validate_scope(scope)
194
+ self.order_free = self.validate_order_free(order_free)
195
+ self.and_condition = self.initialize_and_condition(and_condition)
196
+
197
+ def to_dict(self):
198
+ # Convert the SignatureEntry instance to a dictionary format suitable for inclusion in self.entry
199
+ signature_dict = {
200
+ 'name': self.name,
201
+ 'comment': self.comment,
202
+ 'scope': self.scope,
203
+ 'order-free': self.order_free,
204
+ 'and-condition': {'entry': [condition.to_dict() for condition in self.and_condition] if self.and_condition else None},
205
+ }
206
+ return signature_dict
207
+
208
+ def validate_name(self, name):
209
+ if not isinstance(name, str) or len(name) > 31 or not all(char.isalnum() or char in "._-" for char in name):
210
+ raise ValueError("Invalid name for signature entry.")
211
+
212
+ def validate_scope(self, scope):
213
+ if scope not in ["protocol-data-unit", "session"]:
214
+ raise ValueError("Invalid scope value.")
215
+ return scope
216
+
217
+ def validate_order_free(self, order_free):
218
+ if order_free not in ["yes", "no"]:
219
+ raise ValueError("Invalid order-free value.")
220
+ return order_free