Skip to content

Commit d4bb65d

Browse files
authored
Merge pull request #198 from TomaszSwierszczCA/ngts-support
Add NGTS (Strata Cloud Manager) support - VC-54745
2 parents 5de41a8 + e6d085e commit d4bb65d

9 files changed

Lines changed: 1036 additions & 6 deletions

File tree

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,13 @@ secrets.json
162162
venv
163163
venv3
164164
venv27
165+
.venv
165166
vcert/example_cert.py
166-
credentials
167+
credentials
168+
169+
# Generated certificate material from example scripts / live tests (never commit)
170+
/cert.pem
171+
/cert.key
172+
/key.pem
173+
/chain.pem
174+
/renewed_cert.pem

README-NGTS.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
2+
![Community Supported](https://img.shields.io/badge/Support%20Level-Community-brightgreen)
3+
![Compatible with Palo Alto NGTS](https://img.shields.io/badge/Compatibility-Palo_Alto_NGTS-f9a90c)
4+
5+
_**This open source project is community-supported.** To report a problem or share an idea, use
6+
**[Issues](https://github.com/Venafi/vcert-python/issues)**; and if you have a suggestion for fixing the issue, please include those details, too.
7+
In addition, use **[Pull Requests](https://github.com/Venafi/vcert-python/pulls)** to contribute actual bug fixes or proposed enhancements.
8+
We welcome and appreciate all contributions. Got questions or want to discuss something with our team?
9+
**[Join us on Slack](https://join.slack.com/t/venafi-integrations/shared_invite/zt-i8fwc379-kDJlmzU8OiIQOJFSwiA~dg)**!_
10+
11+
# VCert Python SDK for Palo Alto Networks Next-Gen Trust Security (NGTS)
12+
13+
VCert Python is a library and SDK designed to simplify key generation and certificate
14+
enrollment. This guide covers using it against **Palo Alto Networks Next-Gen Trust Security
15+
(NGTS)**, also known as Strata Cloud Manager.
16+
17+
> 📌 **NOTE:** Unlike the [Go VCert](https://github.com/Venafi/vcert) project, vcert-python is
18+
> **SDK-only** — there is no CLI, playbook, or certificate provisioning. NGTS support in this
19+
> SDK is **certificate-lifecycle only**: `get_policy`/`set_policy`, SSH, and `get_version`
20+
> raise `NotImplementedError`.
21+
22+
## Quick Links
23+
24+
- [Prerequisites](#prerequisites)
25+
- [Connecting](#connecting)
26+
- [Connection Parameters](#connection-parameters)
27+
- [API URL Default and Token URL](#api-url-default-and-token-url)
28+
- [Zone Format](#zone-format)
29+
- [Examples](#examples)
30+
- [Connect with service-account credentials](#connect-with-service-account-credentials)
31+
- [Connect with a pre-issued access token](#connect-with-a-pre-issued-access-token)
32+
- [Request and retrieve a certificate](#request-and-retrieve-a-certificate)
33+
- [Renew a certificate](#renew-a-certificate)
34+
35+
## Prerequisites
36+
37+
1. The Palo Alto Networks NGTS API is reachable from where your code runs. The production
38+
endpoint is `https://api.strata.paloaltonetworks.com/ngts`.
39+
2. A Palo Alto Networks NGTS service account has been registered and granted the permissions
40+
needed for the operations you use. See the
41+
[Palo Alto Networks service account documentation](https://pan.dev/scm/docs/service-accounts/).
42+
Minimum permissions per SDK operation:
43+
44+
| SDK operation | Required NGTS permissions |
45+
|---|---|
46+
| `request_cert` (enroll) | `ngts.certificate_issuing_template.get`, `ngts.certificate_request.create`, `ngts.certificate_request.get`, `ngts.certificate_content.get` |
47+
| `retrieve_cert` (pickup) | `ngts.certificate_request.get`, `ngts.certificate.get`, `ngts.edge_encryption_key.get`, `ngts.certificate_content.get` |
48+
| `renew_cert` | `ngts.certificate.search`, `ngts.certificate_content.get`, `ngts.certificate_request.get`, `ngts.certificate.get`, `ngts.certificate_request.create` |
49+
| `revoke_cert` | _(see your NGTS CA Account configuration)_ |
50+
| `retire_cert` | `ngts.certificate.search`, `ngts.certificate_request.get`, `ngts.certificate.retire` |
51+
52+
3. You have **either** an OAuth access token, **or** service-account credentials (Client ID,
53+
Client Secret, and a TSG ID or scope) the SDK can use to obtain one.
54+
4. A CA Account and an Issuing Template (CIT) exist and are configured with Recommended
55+
Settings (OU, O, L, ST, C) and appropriate Issuing Rules. You know the Issuing Template's
56+
**API alias**.
57+
58+
## Connecting
59+
60+
Use `venafi_connection`. NGTS is selected either explicitly via
61+
`platform=VenafiPlatform.NGTS`, or automatically when `client_id` and `client_secret` are
62+
supplied (these are NGTS-specific, so they aren't shadowed by the TPP/Cloud branches).
63+
64+
```python
65+
from vcert import venafi_connection, VenafiPlatform
66+
67+
# Explicit platform selection (token_url defaults to production; override for non-prod)
68+
conn = venafi_connection(
69+
platform=VenafiPlatform.NGTS,
70+
client_id="<service account client id>",
71+
client_secret="<service account client secret>",
72+
tsg_id="<tenant service group id>",
73+
)
74+
75+
# Auto-detection (client_id + client_secret present)
76+
conn = venafi_connection(
77+
client_id="<service account client id>",
78+
client_secret="<service account client secret>",
79+
tsg_id="<tenant service group id>",
80+
)
81+
```
82+
83+
### Connection Parameters
84+
85+
| Parameter | Required | Description |
86+
|---|---|---|
87+
| `client_id` | yes¹ | Service-account Client ID used to obtain an access token. |
88+
| `client_secret` | yes¹ | Service-account Client Secret used to obtain an access token. |
89+
| `tsg_id` | yes² | Tenant Service Group ID. Used to derive the OAuth scope as `tsg_id:<TSG_ID>`. |
90+
| `scope` | yes² | OAuth scope (e.g. `tsg_id:<TSG_ID>`). Takes precedence over `tsg_id` when supplied. |
91+
| `access_token` | no¹ | A pre-issued OAuth access token. When supplied, `client_id`/`client_secret` become optional (but are still used to refresh the token if present). |
92+
| `token_url` | no | OAuth token endpoint. Defaults to the Palo Alto production endpoint (see below); override it for non-production environments. |
93+
| `url` | no | NGTS API base URL. Defaults to the Palo Alto production endpoint (see below). |
94+
| `http_request_kwargs` | no | Passed through to `requests` (e.g. a trust bundle via `verify`). |
95+
96+
¹ Provide **either** `access_token`, **or** `client_id` + `client_secret`.
97+
² Provide **either** `scope`, **or** `tsg_id` (used to derive the scope).
98+
99+
### API URL and Token URL Defaults
100+
101+
Both `url` (API base URL) and `token_url` (OAuth token endpoint) are optional. When omitted
102+
they default to the published Palo Alto **production** endpoints — supply them only for
103+
non-production environments:
104+
105+
| Parameter | Default |
106+
|---|---|
107+
| `url` | `https://api.strata.paloaltonetworks.com/ngts` |
108+
| `token_url` | `https://auth.apps.paloaltonetworks.com/auth/v1/oauth2/access_token` |
109+
110+
The production token endpoint is taken from the Palo Alto SASE auth API
111+
([reference](https://pan.dev/sase/api/auth/post-auth-v-1-oauth-2-access-token/)).
112+
113+
#### Safeguards around `token_url`
114+
115+
`token_url` is the **credential sink** — your service-account `client_id`/`client_secret` are
116+
exchanged there via HTTP Basic auth — so the SDK guards it without giving up the default:
117+
118+
- **HTTPS is enforced.** An `http://` `token_url` is upgraded to `https://` (with a warning) and
119+
a scheme-less value is assumed to be `https://`, so credentials never travel in cleartext.
120+
- **Defaulting is logged.** Omitting `token_url` logs a **WARNING** before falling back to the
121+
production endpoint. This is intentional: if you target a **non-production** tenant but forget to
122+
set `token_url`, your non-prod credentials would otherwise be sent to the **production** token
123+
endpoint silently. Always set `token_url` explicitly for non-production.
124+
- **Untrusted hosts are flagged.** A `token_url` whose host is outside `.paloaltonetworks.com`
125+
(which covers both production and the documented dev endpoints) logs a **WARNING**, surfacing a
126+
typo'd or hostile endpoint that would leak credentials.
127+
128+
The last two **warn rather than block**, so a future legitimate endpoint on another domain still
129+
works — but you should treat these warnings as a prompt to double-check your configuration.
130+
131+
## Zone Format
132+
133+
For NGTS, the **zone is the Issuing Template (CIT) API alias only**. There is no
134+
`Application\IssuingTemplate` split as there is for CyberArk Certificate Manager, SaaS — the
135+
entire string (trimmed) is the template alias, and a backslash is part of the alias, not a
136+
separator.
137+
138+
```python
139+
zone = "PublicTrust" # the Issuing Template API alias
140+
```
141+
142+
## Examples
143+
144+
For the examples below, assume the Issuing Template has an API alias of `PublicTrust`.
145+
146+
### Connect with service-account credentials
147+
148+
```python
149+
from vcert import venafi_connection, VenafiPlatform
150+
151+
conn = venafi_connection(
152+
platform=VenafiPlatform.NGTS,
153+
token_url="<oauth token endpoint>",
154+
client_id="<client id>",
155+
client_secret="<client secret>",
156+
tsg_id="<tsg id>",
157+
)
158+
```
159+
160+
### Connect with a pre-issued access token
161+
162+
```python
163+
from vcert import venafi_connection, VenafiPlatform
164+
165+
conn = venafi_connection(
166+
platform=VenafiPlatform.NGTS,
167+
access_token="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
168+
)
169+
```
170+
171+
### Request and retrieve a certificate
172+
173+
```python
174+
from vcert import CertificateRequest
175+
176+
zone = "PublicTrust"
177+
request = CertificateRequest(common_name="first-time.venafi.example")
178+
request.san_dns = ["first-san.venafi.example", "second-san.venafi.example"]
179+
180+
conn.request_cert(request, zone)
181+
cert = conn.retrieve_cert(request) # polls until the certificate is issued
182+
183+
print(cert.cert) # end-entity certificate (PEM)
184+
print(cert.chain) # chain certificates (PEM)
185+
print(request.private_key_pem)
186+
```
187+
188+
### Renew a certificate
189+
190+
```python
191+
from vcert import CertificateRequest
192+
193+
# Renew by the enrollment (pickup) id of the existing certificate
194+
request = CertificateRequest(cert_id="{7428fac3-d0e8-4679-9f48-d9e867a326ca}")
195+
conn.renew_cert(request)
196+
cert = conn.retrieve_cert(request)
197+
```
198+
199+
---
200+
201+
For backend-neutral SDK usage (request/retrieve/renew/revoke data objects, output formats),
202+
see the main [README](README.md).

examples/ngts/get_cert_ngts.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright Venafi, Inc. and CyberArk Software Ltd. ("CyberArk")
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
from vcert import (CertificateRequest, venafi_connection, VenafiPlatform)
18+
import string
19+
import random
20+
import logging
21+
from os import environ
22+
23+
logging.basicConfig(level=logging.INFO)
24+
logging.getLogger("urllib3").setLevel(logging.ERROR)
25+
26+
27+
def main():
28+
# Get credentials from environment variables.
29+
# NGTS (Palo Alto Networks Next-Gen Trust Security) authenticates with Strata Cloud Manager
30+
# OAuth2 client credentials issued by a service account. The API base URL and the token URL
31+
# both default to the Palo Alto production endpoints; supply them only for non-production
32+
# environments (unset env vars fall back to None -> the production defaults).
33+
url = environ.get('NGTS_URL') # Optional NGTS API base URL (defaults to production)
34+
token_url = environ.get('NGTS_TOKEN_URL') # Optional OAuth2 token endpoint (defaults to production)
35+
client_id = environ.get('NGTS_CLIENT_ID') # Service-account client id
36+
client_secret = environ.get('NGTS_CLIENT_SECRET') # Service-account client secret
37+
tsg_id = environ.get('NGTS_TSG_ID') # Tenant service group id (used to build the scope)
38+
scope = environ.get('NGTS_SCOPE') # Optional: a ready "tsg_id:<TSG_ID>" scope
39+
zone = environ.get('NGTS_ZONE') # Certificate Issuing Template alias (CIT-only)
40+
41+
# The connection is chosen automatically: when client_id + client_secret are present, an
42+
# NGTS connection is built. The platform can also be set explicitly:
43+
# conn = venafi_connection(platform=VenafiPlatform.NGTS, ...)
44+
conn = venafi_connection(url=url, token_url=token_url, client_id=client_id, client_secret=client_secret,
45+
tsg_id=tsg_id, scope=scope)
46+
47+
# Build a Certificate request
48+
request = CertificateRequest(common_name=f"{random_word(10)}.venafi.example.com")
49+
request.san_dns = ["www.dns.venafi.example.com", "ww1.dns.venafi.example.com"]
50+
51+
# Request the certificate.
52+
conn.request_cert(request, zone)
53+
# Wait for the certificate to be retrieved (until ISSUED or timeout, 180s by default).
54+
cert = conn.retrieve_cert(request)
55+
56+
# Print the certificate
57+
print(cert.full_chain)
58+
# Save it into a file
59+
with open("./cert.pem", "w") as f:
60+
f.write(cert.full_chain)
61+
62+
63+
def random_word(length):
64+
letters = string.ascii_lowercase
65+
return ''.join(random.choice(letters) for i in range(length))
66+
67+
68+
if __name__ == '__main__':
69+
main()

tests/test_env.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,14 @@
4040

4141
TPP_SSH_CADN = environ.get('TPP_SSH_CADN')
4242

43+
# NGTS (Palo Alto Networks Next-Gen Trust Security)
44+
NGTS_URL = environ.get('NGTS_URL')
45+
NGTS_TOKEN_URL = environ.get('NGTS_TOKEN_URL')
46+
NGTS_CLIENT_ID = environ.get('NGTS_CLIENT_ID')
47+
NGTS_CLIENT_SECRET = environ.get('NGTS_CLIENT_SECRET')
48+
NGTS_TSG_ID = environ.get('NGTS_TSG_ID')
49+
NGTS_SCOPE = environ.get('NGTS_SCOPE')
50+
NGTS_ZONE = environ.get('NGTS_ZONE')
51+
4352
if RANDOM_DOMAIN and not isinstance(RANDOM_DOMAIN, text_type):
4453
RANDOM_DOMAIN = RANDOM_DOMAIN.decode()

0 commit comments

Comments
 (0)