mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-20 18:34:20 +00:00
feat(azure): add Deploy-to-Azure Bicep template and certificate auth
This commit is contained in:
1 parent
2eff97c2cc
commit
3be989c5b1
7 files changed
+53
-32
No files matched your search
@@ -294,9 +294,9 @@ Alternatively, point Prowler at a certificate file on disk:
|
||||
prowler azure --certificate-auth --certificate-path /path/to/prowler.key
|
||||
```
|
||||
|
||||
## Service Principal Application Authentication (Recommended)
|
||||
## Service Principal Application Authentication
|
||||
|
||||
This method is required for Prowler Cloud and recommended for Prowler CLI.
|
||||
This client-secret flow is the supported fallback when [Certificate Authentication](#certificate-authentication) is not an option — for example on sovereign clouds where the Deploy to Azure quick-start is unavailable, or when an organisation policy forbids certificate-based service principals. New Prowler Cloud onboardings should prefer certificate authentication.
|
||||
|
||||
### Creating the Service Principal
|
||||
For more information, see [Creating Prowler Service Principal](/user-guide/providers/azure/create-prowler-service-principal).
|
||||
|
||||
@@ -57,7 +57,7 @@ Azure supports two authentication methods in the add-provider wizard. Prowler Cl
|
||||
|
||||
#### Certificate Authentication (Recommended)
|
||||
|
||||
The one-click flow: click **Deploy to Azure** in the wizard and let the Bicep template create the App Registration / Service Principal, the `Reader` role assignment, and the custom `ProwlerRole` with a certificate credential in a single deployment.
|
||||
The quick-start flow: click **Deploy to Azure** in the wizard to start a deployment in the Azure Portal with the Prowler Bicep template pre-loaded. The template creates the App Registration / Service Principal, the `Reader` role assignment, and the custom `ProwlerRole` bound to a certificate credential. You still fill the certificate parameter and confirm the deployment inside the Portal.
|
||||
|
||||
1. In the Azure wizard, select **Certificate Authentication (Recommended)**.
|
||||
2. Click the **Deploy to Azure** button. This opens the Azure Portal deployment blade pre-loaded with the [Prowler Bicep template](https://github.com/prowler-cloud/prowler/blob/master/permissions/templates/azure/bicep/prowler-scan.bicep).
|
||||
|
||||
@@ -1188,13 +1188,15 @@ class AzureProvider(Provider):
|
||||
)
|
||||
# since that exception is not considered as critical, we keep filling another identity fields
|
||||
if isinstance(credentials, CertificateCredential):
|
||||
# `getenv("AZURE_CLIENT_ID", default=client_id)` already
|
||||
# covers both the env-var cert-auth path (env is set) and
|
||||
# the static-credentials path (constructor param supplied
|
||||
# `client_id`). Reaching into `credentials._client_id`
|
||||
# would be the same class of azure-identity private state
|
||||
# we deliberately avoided for the thumbprint.
|
||||
identity.identity_id = getenv("AZURE_CLIENT_ID", default=client_id)
|
||||
# Prefer the explicit constructor param over the ambient
|
||||
# env var: with static credentials the caller has
|
||||
# unambiguously said "this is the client id" and using
|
||||
# the env var could silently substitute another
|
||||
# principal's id if the shell happens to have
|
||||
# `AZURE_CLIENT_ID` set. Fall back to the env var only
|
||||
# for the env-only cert-auth path where `client_id` is
|
||||
# `None`.
|
||||
identity.identity_id = client_id or getenv("AZURE_CLIENT_ID")
|
||||
identity.identity_type = "Service Principal with Certificate"
|
||||
# The SHA-1 thumbprint is computed from the certificate
|
||||
# bytes by `_compute_certificate_thumbprint` at
|
||||
|
||||
+19
-16
@@ -75,9 +75,10 @@ export const AzureCertificateCredentialsForm = ({
|
||||
Certificate Authentication (Recommended)
|
||||
</div>
|
||||
<div className="text-text-neutral-tertiary text-sm">
|
||||
Deploy the Prowler Bicep template to provision the App Registration
|
||||
/ Service Principal and read-only roles in one click, then paste the
|
||||
resulting credentials below.
|
||||
The Deploy to Azure quick-start opens the Azure Portal with the
|
||||
Prowler Bicep template pre-loaded to provision the App Registration /
|
||||
Service Principal and read-only roles. Paste the resulting credentials
|
||||
below when the deployment finishes.
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-1">
|
||||
@@ -98,12 +99,15 @@ export const AzureCertificateCredentialsForm = ({
|
||||
Don't have a certificate yet?
|
||||
</div>
|
||||
<p className="text-text-neutral-tertiary text-xs">
|
||||
Generate a self-signed X.509 certificate right in your browser. The
|
||||
<strong> private key is generated locally</strong> and goes straight
|
||||
into the field below (it is then submitted with the rest of the
|
||||
form). The public certificate downloads as a text file for you to
|
||||
paste into the Bicep <code>Certificate Base64</code> parameter in
|
||||
the Portal.
|
||||
Generate a self-signed X.509 certificate right in your browser.
|
||||
<strong>
|
||||
{" "}
|
||||
The certificate and private key are generated locally
|
||||
</strong>
|
||||
. The base64-encoded certificate/private-key bundle goes into the
|
||||
field below and is submitted with the rest of the form. The public
|
||||
certificate downloads as a text file for you to paste into the Bicep{" "}
|
||||
<code>Certificate Base64</code> parameter in the Portal.
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -112,19 +116,18 @@ export const AzureCertificateCredentialsForm = ({
|
||||
onClick={handleGenerateCertificate}
|
||||
disabled={isGeneratingCert}
|
||||
>
|
||||
{isGeneratingCert
|
||||
? "Generating…"
|
||||
: "Generate certificate for me"}
|
||||
{isGeneratingCert ? "Generating…" : "Generate certificate for me"}
|
||||
</Button>
|
||||
{generatorError && (
|
||||
<p className="text-text-error-primary text-xs">{generatorError}</p>
|
||||
)}
|
||||
{generatedThumbprint && (
|
||||
<p className="text-text-success-primary text-xs">
|
||||
Done. Certificate SHA-1 thumbprint: <code>{generatedThumbprint}</code>
|
||||
. Downloaded <code>prowler-cert-base64.txt</code> — paste its
|
||||
contents into the Bicep <code>Certificate Base64</code> parameter,
|
||||
then keep filling the fields below.
|
||||
Done. Certificate SHA-1 thumbprint:{" "}
|
||||
<code>{generatedThumbprint}</code>. Downloaded{" "}
|
||||
<code>prowler-cert-base64.txt</code> — paste its contents into the
|
||||
Bicep <code>Certificate Base64</code> parameter, then keep filling
|
||||
the fields below.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+15
-1
@@ -6,12 +6,26 @@ import { WizardRadioCard } from "@/components/providers/workflow/forms/fields";
|
||||
import { FormMessage } from "@/components/shadcn/form";
|
||||
import { RadioGroup } from "@/components/shadcn/radio-group/radio-group";
|
||||
|
||||
// The `via` values shared with the M365 flow (they both authenticate against
|
||||
// an Entra ID App Registration and reuse the same wizard state machine).
|
||||
// Exposed as a const object so consumers can reference the values by name
|
||||
// instead of duplicating string literals — matches the AWS/M365 patterns
|
||||
// (`AWS_CREDENTIAL_OPTIONS`, `M365_CREDENTIAL_OPTIONS`).
|
||||
export const AZURE_CREDENTIALS_TYPES = {
|
||||
CERTIFICATE: "app_certificate",
|
||||
CLIENT_SECRET: "app_client_secret",
|
||||
EMPTY: "",
|
||||
} as const;
|
||||
|
||||
type AzureCredentialsType =
|
||||
(typeof AZURE_CREDENTIALS_TYPES)[keyof typeof AZURE_CREDENTIALS_TYPES];
|
||||
|
||||
// Local form shape for this selector: only the radio value lives here, the
|
||||
// actual credential fields belong to the downstream credentials form. Kept
|
||||
// exported so `SelectViaAzure` and the tests bind the same type instead of
|
||||
// falling back to `any`.
|
||||
export type AzureCredentialsTypeFormValues = {
|
||||
azureCredentialsType: "app_certificate" | "app_client_secret" | "";
|
||||
azureCredentialsType: AzureCredentialsType;
|
||||
};
|
||||
|
||||
type RadioGroupAzureViaCredentialsFormProps = {
|
||||
|
||||
@@ -148,11 +148,11 @@ export async function generateProwlerCertificate(
|
||||
// private key second. The full bundle is then base64-encoded so it can
|
||||
// live inside a single form field / JSON payload.
|
||||
const bundlePem = `${certPem.trim()}\n${privateKeyPem.trim()}\n`;
|
||||
const privateKeyBundleBase64Pem = toBase64(new TextEncoder().encode(bundlePem));
|
||||
|
||||
const thumbprintBytes = new Uint8Array(
|
||||
await subtle.digest("SHA-1", certDer),
|
||||
const privateKeyBundleBase64Pem = toBase64(
|
||||
new TextEncoder().encode(bundlePem),
|
||||
);
|
||||
|
||||
const thumbprintBytes = new Uint8Array(await subtle.digest("SHA-1", certDer));
|
||||
const thumbprintHex = bytesToHexUpper(thumbprintBytes);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1102,7 +1102,9 @@ export class ProvidersPage extends BasePage {
|
||||
|
||||
if (type === AZURE_CREDENTIAL_OPTIONS.AZURE_CREDENTIALS) {
|
||||
await this.azureServicePrincipalRadio.click({ force: true });
|
||||
} else if (type === AZURE_CREDENTIAL_OPTIONS.AZURE_CERTIFICATE_CREDENTIALS) {
|
||||
} else if (
|
||||
type === AZURE_CREDENTIAL_OPTIONS.AZURE_CERTIFICATE_CREDENTIALS
|
||||
) {
|
||||
await this.azureCertificateCredentialsRadio.click({ force: true });
|
||||
} else {
|
||||
throw new Error(`Invalid Azure credential type: ${type}`);
|
||||
|
||||
Reference in new issue
Block a user