azuredns: servicediscovery for zones (#2140)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
parent
27fd142ca1
commit
874e3ea023
11 changed files with 540 additions and 248 deletions
|
@ -314,8 +314,6 @@ func displayDNSHelp(w io.Writer, name string) error {
|
|||
ew.writeln(` - "AZURE_CLIENT_CERTIFICATE_PATH": Client certificate path`)
|
||||
ew.writeln(` - "AZURE_CLIENT_ID": Client ID`)
|
||||
ew.writeln(` - "AZURE_CLIENT_SECRET": Client secret`)
|
||||
ew.writeln(` - "AZURE_RESOURCE_GROUP": DNS zone resource group`)
|
||||
ew.writeln(` - "AZURE_SUBSCRIPTION_ID": DNS zone subscription ID`)
|
||||
ew.writeln(` - "AZURE_TENANT_ID": Tenant ID`)
|
||||
ew.writeln()
|
||||
|
||||
|
@ -326,6 +324,9 @@ func displayDNSHelp(w io.Writer, name string) error {
|
|||
ew.writeln(` - "AZURE_POLLING_INTERVAL": Time between DNS propagation check`)
|
||||
ew.writeln(` - "AZURE_PRIVATE_ZONE": Set to true to use Azure Private DNS Zones and not public`)
|
||||
ew.writeln(` - "AZURE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
|
||||
ew.writeln(` - "AZURE_RESOURCE_GROUP": DNS zone resource group`)
|
||||
ew.writeln(` - "AZURE_SERVICEDISCOVERY_FILTER": Advanced ServiceDiscovery filter using Kusto query condition`)
|
||||
ew.writeln(` - "AZURE_SUBSCRIPTION_ID": DNS zone subscription ID`)
|
||||
ew.writeln(` - "AZURE_TTL": The TTL of the TXT record used for the DNS challenge`)
|
||||
ew.writeln(` - "AZURE_ZONE_NAME": Zone name to use inside Azure DNS service to add the TXT record in`)
|
||||
|
||||
|
|
|
@ -48,15 +48,12 @@ lego --domains example.com --email your_example@email.com --dns azuredns run
|
|||
### Using Managed Identity (Azure VM)
|
||||
|
||||
AZURE_TENANT_ID=<your service principal tenant ID> \
|
||||
AZURE_SUBSCRIPTION_ID=<your target zone subscription ID> \
|
||||
AZURE_RESOURCE_GROUP=<your target zone resource group name> \
|
||||
lego --domains example.com --email your_example@email.com --dns azuredns run
|
||||
|
||||
### Using Managed Identity (Azure Arc)
|
||||
|
||||
AZURE_TENANT_ID=<your service principal tenant ID> \
|
||||
AZURE_SUBSCRIPTION_ID=<your target zone subscription ID> \
|
||||
AZURE_RESOURCE_GROUP=<your target zone resource group name> \
|
||||
IMDS_ENDPOINT=http://localhost:40342 \
|
||||
IDENTITY_ENDPOINT=http://localhost:40342/metadata/identity/oauth2/token \
|
||||
lego --domains example.com --email your_example@email.com --dns azuredns run
|
||||
|
@ -73,8 +70,6 @@ lego --domains example.com --email your_example@email.com --dns azuredns run
|
|||
| `AZURE_CLIENT_CERTIFICATE_PATH` | Client certificate path |
|
||||
| `AZURE_CLIENT_ID` | Client ID |
|
||||
| `AZURE_CLIENT_SECRET` | Client secret |
|
||||
| `AZURE_RESOURCE_GROUP` | DNS zone resource group |
|
||||
| `AZURE_SUBSCRIPTION_ID` | DNS zone subscription ID |
|
||||
| `AZURE_TENANT_ID` | Tenant ID |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
|
@ -91,6 +86,9 @@ More information [here]({{< ref "dns#configuration-and-credentials" >}}).
|
|||
| `AZURE_POLLING_INTERVAL` | Time between DNS propagation check |
|
||||
| `AZURE_PRIVATE_ZONE` | Set to true to use Azure Private DNS Zones and not public |
|
||||
| `AZURE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
|
||||
| `AZURE_RESOURCE_GROUP` | DNS zone resource group |
|
||||
| `AZURE_SERVICEDISCOVERY_FILTER` | Advanced ServiceDiscovery filter using Kusto query condition |
|
||||
| `AZURE_SUBSCRIPTION_ID` | DNS zone subscription ID |
|
||||
| `AZURE_TTL` | The TTL of the TXT record used for the DNS challenge |
|
||||
| `AZURE_ZONE_NAME` | Zone name to use inside Azure DNS service to add the TXT record in |
|
||||
|
||||
|
@ -115,6 +113,22 @@ Link:
|
|||
|
||||
### Environment variables
|
||||
|
||||
#### Service Discovery
|
||||
|
||||
Lego automatically finds all visible Azure (private) DNS zones using [Azure ResourceGraph query](https://learn.microsoft.com/en-us/azure/governance/resource-graph/).
|
||||
This can be limited by specifying environment variable `AZURE_SUBSCRIPTION_ID` and/or `AZURE_RESOURCE_GROUP` which limits the
|
||||
DNS zones to only a subscription or to one resourceGroup.
|
||||
|
||||
Additionally environment variable `AZURE_SERVICEDISCOVERY_FILTER` can be used to filter DNS zones with an addition Kusto filter eg:
|
||||
|
||||
```
|
||||
resources
|
||||
| where type =~ "microsoft.network/dnszones"
|
||||
| ${AZURE_SERVICEDISCOVERY_FILTER}
|
||||
| project subscriptionId, resourceGroup, name
|
||||
```
|
||||
|
||||
|
||||
#### Client secret
|
||||
|
||||
The Azure Credentials can be configured using the following environment variables:
|
||||
|
@ -122,7 +136,7 @@ The Azure Credentials can be configured using the following environment variable
|
|||
* AZURE_CLIENT_SECRET = "Client secret"
|
||||
* AZURE_TENANT_ID = "Tenant ID"
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
|
||||
#### Client certificate
|
||||
|
||||
|
@ -131,7 +145,7 @@ The Azure Credentials can be configured using the following environment variable
|
|||
* AZURE_CLIENT_CERTIFICATE_PATH = "Client certificate path"
|
||||
* AZURE_TENANT_ID = "Tenant ID"
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
|
||||
### Workload identity
|
||||
|
||||
|
@ -142,12 +156,12 @@ This must be configured in kubernetes workload deployment in one hand and on the
|
|||
Here is a summary of the steps to follow to use it :
|
||||
* create a `ServiceAccount` resource, add following annotations to reference the targeted Azure AD application registration : `azure.workload.identity/client-id` and `azure.workload.identity/tenant-id`.
|
||||
* on the `Deployment` resource you must reference the previous `ServiceAccount` and add the following label : `azure.workload.identity/use: "true"`.
|
||||
* create a fedreated credentials of type `Kubernetes accessing Azure resources`, add the cluster issuer URL and add the namespace and name of your kubernetes service account.
|
||||
* create a federated credentials of type `Kubernetes accessing Azure resources`, add the cluster issuer URL and add the namespace and name of your kubernetes service account.
|
||||
|
||||
Link :
|
||||
- [Azure AD Workload identity](https://azure.github.io/azure-workload-identity/docs/topics/service-account-labels-and-annotations.html)
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `wli`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `wli`.
|
||||
|
||||
### Azure Managed Identity
|
||||
|
||||
|
@ -182,9 +196,9 @@ az role assignment create \
|
|||
```
|
||||
|
||||
A timeout wrapper is configured for this authentication method.
|
||||
The duraction can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The duration can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The default timeout is 2 seconds.
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
|
||||
#### Azure Managed Identity (with Azure Arc)
|
||||
|
||||
|
@ -198,9 +212,9 @@ you may need to set the environment variables:
|
|||
* `IDENTITY_ENDPOINT=http://localhost:40342/metadata/identity/oauth2/token`
|
||||
|
||||
A timeout wrapper is configured for this authentication method.
|
||||
The duraction can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The duration can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The default timeout is 2 seconds.
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
|
||||
### Azure CLI
|
||||
|
||||
|
@ -208,7 +222,7 @@ The Azure CLI is a command-line tool provided by Microsoft to interact with Azur
|
|||
It provides an easy way to authenticate by simply running `az login` command.
|
||||
The generated token will be cached by default in the `~/.azure` folder.
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `cli`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `cli`.
|
||||
|
||||
### Open ID Connect
|
||||
|
||||
|
|
14
go.mod
14
go.mod
|
@ -7,10 +7,11 @@ go 1.21
|
|||
require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0
|
||||
github.com/Azure/go-autorest/autorest v0.11.29
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0
|
||||
|
@ -88,14 +89,14 @@ require (
|
|||
require (
|
||||
cloud.google.com/go/compute v1.20.1 // indirect
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
|
@ -123,10 +124,11 @@ require (
|
|||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/s2a-go v0.1.4 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
|
@ -144,7 +146,7 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
|
|
30
go.sum
30
go.sum
|
@ -19,18 +19,20 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYs
|
|||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 h1:8iR6OLffWWorFdzL2JFCab5xpD8VKEE2DUBBl+HNTDY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0/go.mod h1:copqlcjMWc/wgQ1N2fzsJFQxDdqKGg1EQt8T5wJMOGE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 h1:rR8ZW79lE/ppfXTfiYSnMFv5EzmVuY4pfZWIkscIJ64=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0/go.mod h1:y2zXtLSMM/X5Mfawq0lOftpWn3f4V6OCsRdINsvWBPI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 h1:zLzoX5+W2l95UJoVwiyNS4dX8vHyQ6x2xRLoBBL9wMk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
|
@ -56,8 +58,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
|
|||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
|
@ -238,6 +240,8 @@ github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
|
|||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -291,8 +295,8 @@ github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkj
|
|||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
|
@ -514,8 +518,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
|
|||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -800,7 +804,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -810,6 +813,7 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
)
|
||||
|
||||
|
@ -40,6 +41,8 @@ const (
|
|||
EnvAuthMethod = envNamespace + "AUTH_METHOD"
|
||||
EnvAuthMSITimeout = envNamespace + "AUTH_MSI_TIMEOUT"
|
||||
|
||||
EnvServiceDiscoveryFilter = envNamespace + "SERVICEDISCOVERY_FILTER"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
|
@ -73,6 +76,8 @@ type Config struct {
|
|||
PollingInterval time.Duration
|
||||
TTL int
|
||||
HTTPClient *http.Client
|
||||
|
||||
ServiceDiscoveryFilter string
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||
|
@ -121,6 +126,8 @@ func NewDNSProvider() (*DNSProvider, error) {
|
|||
config.OIDCToken = env.GetOrFile(EnvOIDCToken)
|
||||
config.OIDCTokenFilePath = env.GetOrFile(EnvOIDCTokenFilePath)
|
||||
|
||||
config.ServiceDiscoveryFilter = env.GetOrFile(EnvServiceDiscoveryFilter)
|
||||
|
||||
oidcValues, _ := env.GetWithFallback(
|
||||
[]string{EnvOIDCRequestURL, EnvGitHubOIDCRequestURL},
|
||||
[]string{EnvOIDCRequestToken, EnvGitHubOIDCRequestToken},
|
||||
|
@ -150,14 +157,6 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
|||
return nil, fmt.Errorf("azuredns: Unable to retrieve valid credentials: %w", err)
|
||||
}
|
||||
|
||||
if config.SubscriptionID == "" {
|
||||
return nil, errors.New("azuredns: SubscriptionID is missing")
|
||||
}
|
||||
|
||||
if config.ResourceGroup == "" {
|
||||
return nil, errors.New("azuredns: ResourceGroup is missing")
|
||||
}
|
||||
|
||||
var dnsProvider challenge.ProviderTimeout
|
||||
if config.PrivateZone {
|
||||
dnsProvider, err = NewDNSProviderPrivate(config, credentials)
|
||||
|
@ -254,7 +253,21 @@ func (w *timeoutTokenCredential) GetToken(ctx context.Context, opts policy.Token
|
|||
return tk, err
|
||||
}
|
||||
|
||||
func deref[T string | int | int32 | int64](v *T) T {
|
||||
func getAuthZone(fqdn string) (string, error) {
|
||||
authZone := env.GetOrFile(EnvZoneName)
|
||||
if authZone != "" {
|
||||
return authZone, nil
|
||||
}
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(fqdn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not find zone: %w", err)
|
||||
}
|
||||
|
||||
return authZone, nil
|
||||
}
|
||||
|
||||
func deref[T any](v *T) T {
|
||||
if v == nil {
|
||||
var zero T
|
||||
return zero
|
||||
|
|
|
@ -27,15 +27,12 @@ lego --domains example.com --email your_example@email.com --dns azuredns run
|
|||
### Using Managed Identity (Azure VM)
|
||||
|
||||
AZURE_TENANT_ID=<your service principal tenant ID> \
|
||||
AZURE_SUBSCRIPTION_ID=<your target zone subscription ID> \
|
||||
AZURE_RESOURCE_GROUP=<your target zone resource group name> \
|
||||
lego --domains example.com --email your_example@email.com --dns azuredns run
|
||||
|
||||
### Using Managed Identity (Azure Arc)
|
||||
|
||||
AZURE_TENANT_ID=<your service principal tenant ID> \
|
||||
AZURE_SUBSCRIPTION_ID=<your target zone subscription ID> \
|
||||
AZURE_RESOURCE_GROUP=<your target zone resource group name> \
|
||||
IMDS_ENDPOINT=http://localhost:40342 \
|
||||
IDENTITY_ENDPOINT=http://localhost:40342/metadata/identity/oauth2/token \
|
||||
lego --domains example.com --email your_example@email.com --dns azuredns run
|
||||
|
@ -61,6 +58,22 @@ Link:
|
|||
|
||||
### Environment variables
|
||||
|
||||
#### Service Discovery
|
||||
|
||||
Lego automatically finds all visible Azure (private) DNS zones using [Azure ResourceGraph query](https://learn.microsoft.com/en-us/azure/governance/resource-graph/).
|
||||
This can be limited by specifying environment variable `AZURE_SUBSCRIPTION_ID` and/or `AZURE_RESOURCE_GROUP` which limits the
|
||||
DNS zones to only a subscription or to one resourceGroup.
|
||||
|
||||
Additionally environment variable `AZURE_SERVICEDISCOVERY_FILTER` can be used to filter DNS zones with an addition Kusto filter eg:
|
||||
|
||||
```
|
||||
resources
|
||||
| where type =~ "microsoft.network/dnszones"
|
||||
| ${AZURE_SERVICEDISCOVERY_FILTER}
|
||||
| project subscriptionId, resourceGroup, name
|
||||
```
|
||||
|
||||
|
||||
#### Client secret
|
||||
|
||||
The Azure Credentials can be configured using the following environment variables:
|
||||
|
@ -68,7 +81,7 @@ The Azure Credentials can be configured using the following environment variable
|
|||
* AZURE_CLIENT_SECRET = "Client secret"
|
||||
* AZURE_TENANT_ID = "Tenant ID"
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
|
||||
#### Client certificate
|
||||
|
||||
|
@ -77,7 +90,7 @@ The Azure Credentials can be configured using the following environment variable
|
|||
* AZURE_CLIENT_CERTIFICATE_PATH = "Client certificate path"
|
||||
* AZURE_TENANT_ID = "Tenant ID"
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `env`.
|
||||
|
||||
### Workload identity
|
||||
|
||||
|
@ -88,12 +101,12 @@ This must be configured in kubernetes workload deployment in one hand and on the
|
|||
Here is a summary of the steps to follow to use it :
|
||||
* create a `ServiceAccount` resource, add following annotations to reference the targeted Azure AD application registration : `azure.workload.identity/client-id` and `azure.workload.identity/tenant-id`.
|
||||
* on the `Deployment` resource you must reference the previous `ServiceAccount` and add the following label : `azure.workload.identity/use: "true"`.
|
||||
* create a fedreated credentials of type `Kubernetes accessing Azure resources`, add the cluster issuer URL and add the namespace and name of your kubernetes service account.
|
||||
* create a federated credentials of type `Kubernetes accessing Azure resources`, add the cluster issuer URL and add the namespace and name of your kubernetes service account.
|
||||
|
||||
Link :
|
||||
- [Azure AD Workload identity](https://azure.github.io/azure-workload-identity/docs/topics/service-account-labels-and-annotations.html)
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `wli`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `wli`.
|
||||
|
||||
### Azure Managed Identity
|
||||
|
||||
|
@ -128,9 +141,9 @@ az role assignment create \
|
|||
```
|
||||
|
||||
A timeout wrapper is configured for this authentication method.
|
||||
The duraction can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The duration can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The default timeout is 2 seconds.
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
|
||||
#### Azure Managed Identity (with Azure Arc)
|
||||
|
||||
|
@ -144,9 +157,9 @@ you may need to set the environment variables:
|
|||
* `IDENTITY_ENDPOINT=http://localhost:40342/metadata/identity/oauth2/token`
|
||||
|
||||
A timeout wrapper is configured for this authentication method.
|
||||
The duraction can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The duration can be configured by setting the `AZURE_AUTH_MSI_TIMEOUT`.
|
||||
The default timeout is 2 seconds.
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `msi`.
|
||||
|
||||
### Azure CLI
|
||||
|
||||
|
@ -154,7 +167,7 @@ The Azure CLI is a command-line tool provided by Microsoft to interact with Azur
|
|||
It provides an easy way to authenticate by simply running `az login` command.
|
||||
The generated token will be cached by default in the `~/.azure` folder.
|
||||
|
||||
This authentication method can be specificaly used by setting the `AZURE_AUTH_METHOD` environment variable to `cli`.
|
||||
This authentication method can be specifically used by setting the `AZURE_AUTH_METHOD` environment variable to `cli`.
|
||||
|
||||
### Open ID Connect
|
||||
|
||||
|
@ -169,10 +182,11 @@ It can be enabled by setting the `AZURE_AUTH_METHOD` environment variable to `oi
|
|||
AZURE_CLIENT_SECRET = "Client secret"
|
||||
AZURE_TENANT_ID = "Tenant ID"
|
||||
AZURE_CLIENT_CERTIFICATE_PATH = "Client certificate path"
|
||||
AZURE_SUBSCRIPTION_ID = "DNS zone subscription ID"
|
||||
AZURE_RESOURCE_GROUP = "DNS zone resource group"
|
||||
[Configuration.Additional]
|
||||
AZURE_ENVIRONMENT = "Azure environment, one of: public, usgovernment, and china"
|
||||
AZURE_SUBSCRIPTION_ID = "DNS zone subscription ID"
|
||||
AZURE_RESOURCE_GROUP = "DNS zone resource group"
|
||||
AZURE_SERVICEDISCOVERY_FILTER = "Advanced ServiceDiscovery filter using Kusto query condition"
|
||||
AZURE_PRIVATE_ZONE = "Set to true to use Azure Private DNS Zones and not public"
|
||||
AZURE_ZONE_NAME = "Zone name to use inside Azure DNS service to add the TXT record in"
|
||||
AZURE_AUTH_METHOD = "Specify which authentication method to use"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package azuredns
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -25,20 +23,10 @@ func TestNewDNSProvider(t *testing.T) {
|
|||
envVars map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
envVars: map[string]string{
|
||||
EnvEnvironment: "",
|
||||
EnvSubscriptionID: "A",
|
||||
EnvResourceGroup: "B",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "unknown environment",
|
||||
envVars: map[string]string{
|
||||
EnvEnvironment: "test",
|
||||
EnvSubscriptionID: "A",
|
||||
EnvResourceGroup: "B",
|
||||
EnvEnvironment: "test",
|
||||
},
|
||||
expected: "azuredns: unknown environment test",
|
||||
},
|
||||
|
@ -67,78 +55,6 @@ func TestNewDNSProvider(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNewDNSProviderConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
subscriptionID string
|
||||
resourceGroup string
|
||||
privateZone bool
|
||||
handler func(w http.ResponseWriter, r *http.Request)
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success (public)",
|
||||
subscriptionID: "A",
|
||||
resourceGroup: "B",
|
||||
privateZone: false,
|
||||
},
|
||||
{
|
||||
desc: "success (private)",
|
||||
subscriptionID: "A",
|
||||
resourceGroup: "B",
|
||||
privateZone: true,
|
||||
},
|
||||
{
|
||||
desc: "SubscriptionID missing",
|
||||
subscriptionID: "",
|
||||
resourceGroup: "",
|
||||
expected: "azuredns: SubscriptionID is missing",
|
||||
},
|
||||
{
|
||||
desc: "ResourceGroup missing",
|
||||
subscriptionID: "A",
|
||||
resourceGroup: "",
|
||||
expected: "azuredns: ResourceGroup is missing",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.SubscriptionID = test.subscriptionID
|
||||
config.ResourceGroup = test.resourceGroup
|
||||
config.PrivateZone = test.privateZone
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
if test.handler == nil {
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {})
|
||||
} else {
|
||||
mux.HandleFunc("/", test.handler)
|
||||
}
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
|
||||
if test.expected != "" {
|
||||
require.EqualError(t, err, test.expected)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.provider)
|
||||
|
||||
if test.privateZone {
|
||||
assert.IsType(t, p.provider, new(DNSProviderPrivate))
|
||||
} else {
|
||||
assert.IsType(t, p.provider, new(DNSProviderPublic))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLivePresent(t *testing.T) {
|
||||
if !envTest.IsLiveTest() {
|
||||
t.Skip("skipping live test")
|
||||
|
|
|
@ -9,40 +9,30 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
)
|
||||
|
||||
// DNSProviderPrivate implements the challenge.Provider interface for Azure Private Zone DNS.
|
||||
type DNSProviderPrivate struct {
|
||||
config *Config
|
||||
zoneClient *armprivatedns.PrivateZonesClient
|
||||
recordClient *armprivatedns.RecordSetsClient
|
||||
config *Config
|
||||
credentials azcore.TokenCredential
|
||||
serviceDiscoveryZones map[string]ServiceDiscoveryZone
|
||||
}
|
||||
|
||||
// NewDNSProviderPrivate creates a DNSProviderPrivate structure with initialized Azure clients.
|
||||
// NewDNSProviderPrivate creates a DNSProviderPrivate structure.
|
||||
func NewDNSProviderPrivate(config *Config, credentials azcore.TokenCredential) (*DNSProviderPrivate, error) {
|
||||
options := arm.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: config.Environment,
|
||||
},
|
||||
}
|
||||
|
||||
zoneClient, err := armprivatedns.NewPrivateZonesClient(config.SubscriptionID, credentials, &options)
|
||||
zones, err := discoverDNSZones(context.Background(), config, credentials)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recordClient, err := armprivatedns.NewRecordSetsClient(config.SubscriptionID, credentials, &options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("discover DNS zones: %w", err)
|
||||
}
|
||||
|
||||
return &DNSProviderPrivate{
|
||||
config: config,
|
||||
zoneClient: zoneClient,
|
||||
recordClient: recordClient,
|
||||
config: config,
|
||||
credentials: credentials,
|
||||
serviceDiscoveryZones: zones,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -57,18 +47,23 @@ func (d *DNSProviderPrivate) Present(domain, _, keyAuth string) error {
|
|||
ctx := context.Background()
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN)
|
||||
zone, err := d.getHostedZone(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone)
|
||||
client, err := newPrivateZoneClient(zone, d.credentials, d.config.Environment)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
// Get existing record set
|
||||
rset, err := d.recordClient.Get(ctx, d.config.ResourceGroup, zone, armprivatedns.RecordTypeTXT, subDomain, nil)
|
||||
resp, err := client.Get(ctx, subDomain)
|
||||
if err != nil {
|
||||
var respErr *azcore.ResponseError
|
||||
if !errors.As(err, &respErr) || respErr.StatusCode != http.StatusNotFound {
|
||||
|
@ -77,32 +72,23 @@ func (d *DNSProviderPrivate) Present(domain, _, keyAuth string) error {
|
|||
}
|
||||
|
||||
// Construct unique TXT records using map
|
||||
uniqRecords := map[string]struct{}{info.Value: {}}
|
||||
if rset.RecordSet.Properties != nil && rset.RecordSet.Properties.TxtRecords != nil {
|
||||
for _, txtRecord := range rset.RecordSet.Properties.TxtRecords {
|
||||
// Assume Value doesn't contain multiple strings
|
||||
if len(txtRecord.Value) > 0 {
|
||||
uniqRecords[deref(txtRecord.Value[0])] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
uniqRecords := privateUniqueRecords(resp.RecordSet, info.Value)
|
||||
|
||||
var txtRecords []*armprivatedns.TxtRecord
|
||||
for txt := range uniqRecords {
|
||||
txtRecord := txt
|
||||
txtRecords = append(txtRecords, &armprivatedns.TxtRecord{Value: []*string{&txtRecord}})
|
||||
txtRecords = append(txtRecords, &armprivatedns.TxtRecord{Value: to.SliceOfPtrs(txtRecord)})
|
||||
}
|
||||
|
||||
ttlInt64 := int64(d.config.TTL)
|
||||
rec := armprivatedns.RecordSet{
|
||||
Name: &subDomain,
|
||||
Properties: &armprivatedns.RecordSetProperties{
|
||||
TTL: &ttlInt64,
|
||||
TTL: to.Ptr(int64(d.config.TTL)),
|
||||
TxtRecords: txtRecords,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = d.recordClient.CreateOrUpdate(ctx, d.config.ResourceGroup, zone, armprivatedns.RecordTypeTXT, subDomain, rec, nil)
|
||||
_, err = client.CreateOrUpdate(ctx, subDomain, rec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
@ -115,17 +101,22 @@ func (d *DNSProviderPrivate) CleanUp(domain, _, keyAuth string) error {
|
|||
ctx := context.Background()
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN)
|
||||
zone, err := d.getHostedZone(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone)
|
||||
client, err := newPrivateZoneClient(zone, d.credentials, d.config.Environment)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
_, err = d.recordClient.Delete(ctx, d.config.ResourceGroup, zone, armprivatedns.RecordTypeTXT, subDomain, nil)
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
_, err = client.Delete(ctx, subDomain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
@ -134,21 +125,67 @@ func (d *DNSProviderPrivate) CleanUp(domain, _, keyAuth string) error {
|
|||
}
|
||||
|
||||
// Checks that azure has a zone for this domain name.
|
||||
func (d *DNSProviderPrivate) getHostedZoneID(ctx context.Context, fqdn string) (string, error) {
|
||||
if zone := env.GetOrFile(EnvZoneName); zone != "" {
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(fqdn)
|
||||
func (d *DNSProviderPrivate) getHostedZone(fqdn string) (ServiceDiscoveryZone, error) {
|
||||
authZone, err := getAuthZone(fqdn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not find zone: %w", err)
|
||||
return ServiceDiscoveryZone{}, err
|
||||
}
|
||||
|
||||
zone, err := d.zoneClient.Get(ctx, d.config.ResourceGroup, dns01.UnFqdn(authZone), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
azureZone, exists := d.serviceDiscoveryZones[dns01.UnFqdn(authZone)]
|
||||
if !exists {
|
||||
return ServiceDiscoveryZone{}, fmt.Errorf("could not find zone (from discovery): %s", authZone)
|
||||
}
|
||||
|
||||
// zone.Name shouldn't have a trailing dot(.)
|
||||
return dns01.UnFqdn(deref(zone.Name)), nil
|
||||
return azureZone, nil
|
||||
}
|
||||
|
||||
// privateZoneClient provides Azure client for one DNS zone.
|
||||
type privateZoneClient struct {
|
||||
zone ServiceDiscoveryZone
|
||||
recordClient *armprivatedns.RecordSetsClient
|
||||
}
|
||||
|
||||
// newPrivateZoneClient creates privateZoneClient structure with initialized Azure client.
|
||||
func newPrivateZoneClient(zone ServiceDiscoveryZone, credential azcore.TokenCredential, environment cloud.Configuration) (*privateZoneClient, error) {
|
||||
options := &arm.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: environment,
|
||||
},
|
||||
}
|
||||
|
||||
recordClient, err := armprivatedns.NewRecordSetsClient(zone.SubscriptionID, credential, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &privateZoneClient{
|
||||
zone: zone,
|
||||
recordClient: recordClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c privateZoneClient) Get(ctx context.Context, subDomain string) (armprivatedns.RecordSetsClientGetResponse, error) {
|
||||
return c.recordClient.Get(ctx, c.zone.ResourceGroup, c.zone.Name, armprivatedns.RecordTypeTXT, subDomain, nil)
|
||||
}
|
||||
|
||||
func (c privateZoneClient) CreateOrUpdate(ctx context.Context, subDomain string, rec armprivatedns.RecordSet) (armprivatedns.RecordSetsClientCreateOrUpdateResponse, error) {
|
||||
return c.recordClient.CreateOrUpdate(ctx, c.zone.ResourceGroup, c.zone.Name, armprivatedns.RecordTypeTXT, subDomain, rec, nil)
|
||||
}
|
||||
|
||||
func (c privateZoneClient) Delete(ctx context.Context, subDomain string) (armprivatedns.RecordSetsClientDeleteResponse, error) {
|
||||
return c.recordClient.Delete(ctx, c.zone.ResourceGroup, c.zone.Name, armprivatedns.RecordTypeTXT, subDomain, nil)
|
||||
}
|
||||
|
||||
func privateUniqueRecords(recordSet armprivatedns.RecordSet, value string) map[string]struct{} {
|
||||
uniqRecords := map[string]struct{}{value: {}}
|
||||
if recordSet.Properties != nil && recordSet.Properties.TxtRecords != nil {
|
||||
for _, txtRecord := range recordSet.Properties.TxtRecords {
|
||||
// Assume Value doesn't contain multiple strings
|
||||
if len(txtRecord.Value) > 0 {
|
||||
uniqRecords[deref(txtRecord.Value[0])] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uniqRecords
|
||||
}
|
||||
|
|
|
@ -9,40 +9,30 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns"
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
)
|
||||
|
||||
// DNSProviderPublic implements the challenge.Provider interface for Azure Public Zone DNS.
|
||||
type DNSProviderPublic struct {
|
||||
config *Config
|
||||
zoneClient *armdns.ZonesClient
|
||||
recordClient *armdns.RecordSetsClient
|
||||
config *Config
|
||||
credentials azcore.TokenCredential
|
||||
serviceDiscoveryZones map[string]ServiceDiscoveryZone
|
||||
}
|
||||
|
||||
// NewDNSProviderPublic creates a DNSProviderPublic structure with intialised Azure clients.
|
||||
// NewDNSProviderPublic creates a DNSProviderPublic structure.
|
||||
func NewDNSProviderPublic(config *Config, credentials azcore.TokenCredential) (*DNSProviderPublic, error) {
|
||||
options := arm.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: config.Environment,
|
||||
},
|
||||
}
|
||||
|
||||
zoneClient, err := armdns.NewZonesClient(config.SubscriptionID, credentials, &options)
|
||||
zones, err := discoverDNSZones(context.Background(), config, credentials)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recordClient, err := armdns.NewRecordSetsClient(config.SubscriptionID, credentials, &options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("discover DNS zones: %w", err)
|
||||
}
|
||||
|
||||
return &DNSProviderPublic{
|
||||
config: config,
|
||||
zoneClient: zoneClient,
|
||||
recordClient: recordClient,
|
||||
config: config,
|
||||
credentials: credentials,
|
||||
serviceDiscoveryZones: zones,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -57,18 +47,23 @@ func (d *DNSProviderPublic) Present(domain, _, keyAuth string) error {
|
|||
ctx := context.Background()
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN)
|
||||
zone, err := d.getHostedZone(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone)
|
||||
client, err := newPublicZoneClient(zone, d.credentials, d.config.Environment)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
// Get existing record set
|
||||
rset, err := d.recordClient.Get(ctx, d.config.ResourceGroup, zone, subDomain, armdns.RecordTypeTXT, nil)
|
||||
resp, err := client.Get(ctx, subDomain)
|
||||
if err != nil {
|
||||
var respErr *azcore.ResponseError
|
||||
if !errors.As(err, &respErr) || respErr.StatusCode != http.StatusNotFound {
|
||||
|
@ -76,33 +71,23 @@ func (d *DNSProviderPublic) Present(domain, _, keyAuth string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Construct unique TXT records using map
|
||||
uniqRecords := map[string]struct{}{info.Value: {}}
|
||||
if rset.RecordSet.Properties != nil && rset.RecordSet.Properties.TxtRecords != nil {
|
||||
for _, txtRecord := range rset.RecordSet.Properties.TxtRecords {
|
||||
// Assume Value doesn't contain multiple strings
|
||||
if len(txtRecord.Value) > 0 {
|
||||
uniqRecords[deref(txtRecord.Value[0])] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
uniqRecords := publicUniqueRecords(resp.RecordSet, info.Value)
|
||||
|
||||
var txtRecords []*armdns.TxtRecord
|
||||
for txt := range uniqRecords {
|
||||
txtRecord := txt
|
||||
txtRecords = append(txtRecords, &armdns.TxtRecord{Value: []*string{&txtRecord}})
|
||||
txtRecords = append(txtRecords, &armdns.TxtRecord{Value: to.SliceOfPtrs(txtRecord)})
|
||||
}
|
||||
|
||||
ttlInt64 := int64(d.config.TTL)
|
||||
rec := armdns.RecordSet{
|
||||
Name: &subDomain,
|
||||
Properties: &armdns.RecordSetProperties{
|
||||
TTL: &ttlInt64,
|
||||
TTL: to.Ptr(int64(d.config.TTL)),
|
||||
TxtRecords: txtRecords,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = d.recordClient.CreateOrUpdate(ctx, d.config.ResourceGroup, zone, subDomain, armdns.RecordTypeTXT, rec, nil)
|
||||
_, err = client.CreateOrUpdate(ctx, subDomain, rec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
@ -115,17 +100,22 @@ func (d *DNSProviderPublic) CleanUp(domain, _, keyAuth string) error {
|
|||
ctx := context.Background()
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN)
|
||||
zone, err := d.getHostedZone(info.EffectiveFQDN)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone)
|
||||
client, err := newPublicZoneClient(zone, d.credentials, d.config.Environment)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
_, err = d.recordClient.Delete(ctx, d.config.ResourceGroup, zone, subDomain, armdns.RecordTypeTXT, nil)
|
||||
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
||||
_, err = client.Delete(ctx, subDomain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("azuredns: %w", err)
|
||||
}
|
||||
|
@ -134,21 +124,66 @@ func (d *DNSProviderPublic) CleanUp(domain, _, keyAuth string) error {
|
|||
}
|
||||
|
||||
// Checks that azure has a zone for this domain name.
|
||||
func (d *DNSProviderPublic) getHostedZoneID(ctx context.Context, fqdn string) (string, error) {
|
||||
if zone := env.GetOrFile(EnvZoneName); zone != "" {
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
authZone, err := dns01.FindZoneByFqdn(fqdn)
|
||||
func (d *DNSProviderPublic) getHostedZone(fqdn string) (ServiceDiscoveryZone, error) {
|
||||
authZone, err := getAuthZone(fqdn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not find zone: %w", err)
|
||||
return ServiceDiscoveryZone{}, err
|
||||
}
|
||||
|
||||
zone, err := d.zoneClient.Get(ctx, d.config.ResourceGroup, dns01.UnFqdn(authZone), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
azureZone, exists := d.serviceDiscoveryZones[dns01.UnFqdn(authZone)]
|
||||
if !exists {
|
||||
return ServiceDiscoveryZone{}, fmt.Errorf("could not find zone (from discovery): %s", authZone)
|
||||
}
|
||||
|
||||
// zone.Name shouldn't have a trailing dot(.)
|
||||
return dns01.UnFqdn(deref(zone.Name)), nil
|
||||
return azureZone, nil
|
||||
}
|
||||
|
||||
type publicZoneClient struct {
|
||||
zone ServiceDiscoveryZone
|
||||
recordClient *armdns.RecordSetsClient
|
||||
}
|
||||
|
||||
// newPublicZoneClient creates publicZoneClient structure with initialized Azure client.
|
||||
func newPublicZoneClient(zone ServiceDiscoveryZone, credential azcore.TokenCredential, environment cloud.Configuration) (*publicZoneClient, error) {
|
||||
options := &arm.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: environment,
|
||||
},
|
||||
}
|
||||
|
||||
recordClient, err := armdns.NewRecordSetsClient(zone.SubscriptionID, credential, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &publicZoneClient{
|
||||
zone: zone,
|
||||
recordClient: recordClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c publicZoneClient) Get(ctx context.Context, subDomain string) (armdns.RecordSetsClientGetResponse, error) {
|
||||
return c.recordClient.Get(ctx, c.zone.ResourceGroup, c.zone.Name, subDomain, armdns.RecordTypeTXT, nil)
|
||||
}
|
||||
|
||||
func (c publicZoneClient) CreateOrUpdate(ctx context.Context, subDomain string, rec armdns.RecordSet) (armdns.RecordSetsClientCreateOrUpdateResponse, error) {
|
||||
return c.recordClient.CreateOrUpdate(ctx, c.zone.ResourceGroup, c.zone.Name, subDomain, armdns.RecordTypeTXT, rec, nil)
|
||||
}
|
||||
|
||||
func (c publicZoneClient) Delete(ctx context.Context, subDomain string) (armdns.RecordSetsClientDeleteResponse, error) {
|
||||
return c.recordClient.Delete(ctx, c.zone.ResourceGroup, c.zone.Name, subDomain, armdns.RecordTypeTXT, nil)
|
||||
}
|
||||
|
||||
func publicUniqueRecords(recordSet armdns.RecordSet, value string) map[string]struct{} {
|
||||
uniqRecords := map[string]struct{}{value: {}}
|
||||
if recordSet.Properties != nil && recordSet.Properties.TxtRecords != nil {
|
||||
for _, txtRecord := range recordSet.Properties.TxtRecords {
|
||||
// Assume Value doesn't contain multiple strings
|
||||
if len(txtRecord.Value) > 0 {
|
||||
uniqRecords[deref(txtRecord.Value[0])] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uniqRecords
|
||||
}
|
||||
|
|
126
providers/dns/azuredns/servicediscovery.go
Normal file
126
providers/dns/azuredns/servicediscovery.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
package azuredns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph"
|
||||
)
|
||||
|
||||
type ServiceDiscoveryZone struct {
|
||||
Name string
|
||||
SubscriptionID string
|
||||
ResourceGroup string
|
||||
}
|
||||
|
||||
const (
|
||||
ResourceGraphTypePublicDNSZone = "microsoft.network/dnszones"
|
||||
ResourceGraphTypePrivateDNSZone = "microsoft.network/privatednszones"
|
||||
)
|
||||
|
||||
const ResourceGraphQueryOptionsTop int32 = 1000
|
||||
|
||||
// discoverDNSZones finds all visible Azure DNS zones based on optional subscriptionID, resourceGroup and serviceDiscovery filter using Kusto query.
|
||||
func discoverDNSZones(ctx context.Context, config *Config, credentials azcore.TokenCredential) (map[string]ServiceDiscoveryZone, error) {
|
||||
options := &arm.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: config.Environment,
|
||||
},
|
||||
}
|
||||
|
||||
client, err := armresourcegraph.NewClient(credentials, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set options
|
||||
requestOptions := &armresourcegraph.QueryRequestOptions{
|
||||
ResultFormat: to.Ptr(armresourcegraph.ResultFormatObjectArray),
|
||||
Top: to.Ptr(ResourceGraphQueryOptionsTop),
|
||||
Skip: to.Ptr[int32](0),
|
||||
}
|
||||
|
||||
zones := map[string]ServiceDiscoveryZone{}
|
||||
for {
|
||||
// create the query request
|
||||
request := armresourcegraph.QueryRequest{
|
||||
Query: to.Ptr(createGraphQuery(config)),
|
||||
Options: requestOptions,
|
||||
}
|
||||
|
||||
result, err := client.Resources(ctx, request, nil)
|
||||
if err != nil {
|
||||
return zones, err
|
||||
}
|
||||
|
||||
resultList, ok := result.Data.([]any)
|
||||
if !ok {
|
||||
// got invalid or empty data, skipping
|
||||
break
|
||||
}
|
||||
|
||||
for _, row := range resultList {
|
||||
rowData, ok := row.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
zoneName, ok := rowData["name"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, exists := zones[zoneName]; exists {
|
||||
return zones, fmt.Errorf(`found duplicate dns zone "%s"`, zoneName)
|
||||
}
|
||||
|
||||
zones[zoneName] = ServiceDiscoveryZone{
|
||||
Name: zoneName,
|
||||
ResourceGroup: rowData["resourceGroup"].(string),
|
||||
SubscriptionID: rowData["subscriptionId"].(string),
|
||||
}
|
||||
}
|
||||
|
||||
*requestOptions.Skip += ResourceGraphQueryOptionsTop
|
||||
|
||||
if result.TotalRecords != nil {
|
||||
if int64(deref(requestOptions.Skip)) >= deref(result.TotalRecords) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return zones, nil
|
||||
}
|
||||
|
||||
func createGraphQuery(config *Config) string {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteString("\nresources\n")
|
||||
|
||||
resourceType := ResourceGraphTypePublicDNSZone
|
||||
if config.PrivateZone {
|
||||
resourceType = ResourceGraphTypePrivateDNSZone
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(buf, "| where type =~ %q\n", resourceType)
|
||||
|
||||
if config.SubscriptionID != "" {
|
||||
_, _ = fmt.Fprintf(buf, "| where subscriptionId =~ %q\n", config.SubscriptionID)
|
||||
}
|
||||
|
||||
if config.ResourceGroup != "" {
|
||||
_, _ = fmt.Fprintf(buf, "| where resourceGroup =~ %q\n", config.ResourceGroup)
|
||||
}
|
||||
|
||||
if config.ServiceDiscoveryFilter != "" {
|
||||
_, _ = fmt.Fprintf(buf, "| %s\n", config.ServiceDiscoveryFilter)
|
||||
}
|
||||
|
||||
buf.WriteString("| project subscriptionId, resourceGroup, name")
|
||||
|
||||
return buf.String()
|
||||
}
|
130
providers/dns/azuredns/servicediscovery_test.go
Normal file
130
providers/dns/azuredns/servicediscovery_test.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package azuredns
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_createGraphQuery(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cfg *Config
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "empty configuration (public)",
|
||||
cfg: &Config{},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/dnszones"
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "SubscriptionID (public)",
|
||||
cfg: &Config{
|
||||
SubscriptionID: "123",
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/dnszones"
|
||||
| where subscriptionId =~ "123"
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "ResourceGroup (public)",
|
||||
cfg: &Config{
|
||||
ResourceGroup: "123",
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/dnszones"
|
||||
| where resourceGroup =~ "123"
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "ServiceDiscoveryFilter (public)",
|
||||
cfg: &Config{
|
||||
ServiceDiscoveryFilter: "123",
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/dnszones"
|
||||
| 123
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "empty configuration (private)",
|
||||
cfg: &Config{
|
||||
PrivateZone: true,
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/privatednszones"
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "SubscriptionID (private)",
|
||||
cfg: &Config{
|
||||
SubscriptionID: "123",
|
||||
PrivateZone: true,
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/privatednszones"
|
||||
| where subscriptionId =~ "123"
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "ResourceGroup (private)",
|
||||
cfg: &Config{
|
||||
ResourceGroup: "123",
|
||||
PrivateZone: true,
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/privatednszones"
|
||||
| where resourceGroup =~ "123"
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "ServiceDiscoveryFilter (private)",
|
||||
cfg: &Config{
|
||||
ServiceDiscoveryFilter: "123",
|
||||
PrivateZone: true,
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/privatednszones"
|
||||
| 123
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
{
|
||||
desc: "all (private)",
|
||||
cfg: &Config{
|
||||
SubscriptionID: "123",
|
||||
ResourceGroup: "456",
|
||||
ServiceDiscoveryFilter: "789",
|
||||
PrivateZone: true,
|
||||
},
|
||||
expected: `
|
||||
resources
|
||||
| where type =~ "microsoft.network/privatednszones"
|
||||
| where subscriptionId =~ "123"
|
||||
| where resourceGroup =~ "456"
|
||||
| 789
|
||||
| project subscriptionId, resourceGroup, name`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
query := createGraphQuery(test.cfg)
|
||||
assert.Equal(t, strings.ReplaceAll(test.expected, "\r", ""), query)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue