To ensure the repository can securely deploy resources to Azure, the following steps are taken:
AZURE_CLIENT_ID
: The client ID of the User Managed Identity.AZURE_TENANT_ID
: The tenant ID that the User Managed Identity exists in.AZURE_SUBSCRIPTION_ID
: The subscription ID where the resources are to be deployed.- name: Azure login
uses: azure/login@v2
with:
client-id: $
tenant-id: $
subscription-id: $
The Azure Storage Account and Blobs have several security measures applied:
HTTPS Traffic Only: The property supportsHttpsTrafficOnly
is set to true
. This means that all requests to this storage account must be made over HTTPS. Any requests made over HTTP will be rejected.
OAuth Authentication: The property defaultToOAuthAuthentication
is set to true
. This means that by default, OAuth is used for authentication. OAuth is a standard protocol that allows users to authenticate without sharing their password, providing a more secure way to control access.
Hierarchical Namespace (HNS) Enabled: The property isHnsEnabled
is set to true
. This means that the Azure Data Lake Storage Gen2 features, which include a hierarchical namespace and access control lists, are enabled for the storage account.
The Azure Function is secured in several ways:
HTTPS Only: The property httpsOnly
is set to true
. This means that all requests to this function app must be made over HTTPS. Any requests made over HTTP will be rejected.
Managed Identity: The function app is assigned a system-managed identity. This allows the function app to authenticate to other Azure services using Azure Active Directory, without needing to store credentials in the code.
Role Assignment: The function app is given the role of ‘Storage Blob Data Contributor’ on the storage account. This means that the function app can read, write, and delete blobs in the storage account, but it does not have permission to manage the storage account itself. No other access is granted to the Function.
Authentication Settings: The function app is configured to require authentication (requireAuthentication: true
). Unauthenticated requests will receive a 401 Unauthorized response (unauthenticatedClientAction: 'Return401'
). The function app is configured to use Entra for authentication (identityProviders.azureActiveDirectory.enabled: true
).
Token Store: The function app is configured to use a token store (tokenStore.enabled: true
). This allows the function app to securely store and retrieve access tokens. The tokenRefreshExtensionHours
is set to 72 hours, which means that the function app will automatically refresh access tokens that are within 72 hours of expiring.
CORS: The function app is configured to only allow CORS requests from ‘https://github.com’ (siteConfig.cors.allowedOrigins: ['https://github.com']
). This can help to prevent cross-site request forgery attacks.
Nonce Validation: The validateNonce
property is set to true
, which means that the function app will validate the nonce value in the ID token that it receives from Azure AD. This can help to prevent replay attacks.
Forward Proxy: The convention is set to NoProxy, which means that the function app will not use a forward proxy for outgoing HTTP requests.
Cookie Expiration: The cookieExpiration is set to a fixed time of 8 hours (timeToExpiration: ‘08:00:00’). This means that authentication cookies will expire 8 hours after they are issued
App registration: The solution is restricted to logins being submitted to a specific client ID.
To successfully call a function hosted in this Function App, you need the following:
Correct verb: The function is expecting a PUT request.
HTTPS only: The function app is configured to only accept HTTPS requests.
Authentication: The function app is configured to require authentication. You need to include an access token in the Authorization header of the request.
To support authentication by the pipeline, you need to get the app registration’s client ID plus the tenant ID and add them as secrets in the repository.
Then, within the pipeline you will need to login to Entra as the pipeline. The --allow-no-subscriptions
flag is used to allow the pipeline to login even if though it won’t be connected to any subscriptions.
- name: Azure login
uses: azure/login@v2
with:
# This is an app registration client ID associated with the shipper function.
client-id: $
tenant-id: $
allow-no-subscriptions: true
As the Function will need a Bearer token to authenticate, you will need to get a token from Entra that you can then pass as a header of the request. You can use the Azure CLI to get an access token.
- name: Set environment variables
run: |
echo "ACCESS_TOKEN=$(az account get-access-token --query accessToken --resource $ -o tsv)" >> $GITHUB_ENV
You can then use the token in the Authorization header of the request.
- name: Send report
run: |
response=$(curl -X $ -v \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $" \
--data @$ \
"$")
The app registration is configured with the following settings:
An example manifest for the app registration:
{
"id": "<guid>",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": null,
"addIns": [],
"allowPublicClient": null,
"appId": "<guid>",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2024-04-28T16:31:03Z",
"description": null,
"certification": null,
"disabledByMicrosoftStatus": null,
"groupMembershipClaims": null,
"identifierUris": [],
"informationalUrls": {
"termsOfService": null,
"support": null,
"privacy": null,
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": null,
"logoutUrl": null,
"name": "Appcat DL Shipper",
"notes": null,
"oauth2AllowIdTokenImplicitFlow": false,
"oauth2AllowImplicitFlow": false,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": null,
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [],
"preAuthorizedApplications": [],
"publisherDomain": "fdpo.onmicrosoft.com",
"replyUrlsWithType": [],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000",
"resourceAccess": [
{
"id": "<guid>",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": null,
"signInAudience": "AzureADMyOrg",
"tags": [
"apiConsumer",
"backgroundProcess"
],
"tokenEncryptionKeyId": null
}