diff --git a/README.md b/README.md index 6ff36fa6..7cf67dbd 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Prometheus exporter for Windows machines, using the WMI (Windows Management Inst Name | Description | Enabled by default ---------|-------------|-------------------- [ad](docs/collector.ad.md) | Active Directory Domain Services | +[adfs](docs/collector.adfs.md) | Active Directory Federation Services | [cpu](docs/collector.cpu.md) | CPU usage | ✓ [cs](docs/collector.cs.md) | "Computer System" metrics (system properties, num cpus/total memory) | ✓ [container](docs/collector.container.md) | Container metrics | diff --git a/collector/adfs.go b/collector/adfs.go new file mode 100644 index 00000000..673da086 --- /dev/null +++ b/collector/adfs.go @@ -0,0 +1,190 @@ +// +build windows + +package collector + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +func init() { + Factories["adfs"] = newADFSCollector +} + +type adfsCollector struct { + adLoginConnectionFailures *prometheus.Desc + certificateAuthentications *prometheus.Desc + deviceAuthentications *prometheus.Desc + extranetAccountLockouts *prometheus.Desc + federatedAuthentications *prometheus.Desc + passportAuthentications *prometheus.Desc + passiveRequests *prometheus.Desc + passwordChangeFailed *prometheus.Desc + passwordChangeSucceeded *prometheus.Desc + tokenRequests *prometheus.Desc + windowsIntegratedAuthentications *prometheus.Desc +} + +// newADFSCollector constructs a new adfsCollector +func newADFSCollector() (Collector, error) { + const subsystem = "adfs" + + return &adfsCollector{ + adLoginConnectionFailures: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "ad_login_connection_failures"), + "Total number of connection failures to an Active Directory domain controller", + nil, + nil, + ), + certificateAuthentications: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "certificate_authentications"), + "Total number of User Certificate authentications", + nil, + nil, + ), + deviceAuthentications: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "device_authentications"), + "Total number of Device authentications", + nil, + nil, + ), + extranetAccountLockouts: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "extranet_account_lockouts"), + "Total number of Extranet Account Lockouts", + nil, + nil, + ), + federatedAuthentications: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "federated_authentications"), + "Total number of authentications from a federated source", + nil, + nil, + ), + passportAuthentications: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "passport_authentications"), + "Total number of Microsoft Passport SSO authentications", + nil, + nil, + ), + passiveRequests: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "passive_requests"), + "Total number of passive (browser-based) requests", + nil, + nil, + ), + passwordChangeFailed: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "password_change_failed"), + "Total number of failed password changes", + nil, + nil, + ), + passwordChangeSucceeded: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "password_change_succeeded"), + "Total number of successful password changes", + nil, + nil, + ), + tokenRequests: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "token_requests"), + "Total number of token requests", + nil, + nil, + ), + windowsIntegratedAuthentications: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "windows_integrated_authentications"), + "Total number of Windows integrated authentications (Kerberos/NTLM)", + nil, + nil, + ), + }, nil +} + +type perflibADFS struct { + AdLoginConnectionFailures float64 `perflib:"AD login Connection Failures"` + CertificateAuthentications float64 `perflib:"Certificate Authentications"` + DeviceAuthentications float64 `perflib:"Device Authentications"` + ExtranetAccountLockouts float64 `perflib:"Extranet Account Lockouts"` + FederatedAuthentications float64 `perflib:"Federated Authentications"` + PassportAuthentications float64 `perflib:"Microsoft Passport Authentications"` + PassiveRequests float64 `perflib:"Passive Requests"` + PasswordChangeFailed float64 `perflib:"Password Change Failed Requests"` + PasswordChangeSucceeded float64 `perflib:"Password Change Successful Requests"` + TokenRequests float64 `perflib:"Token Requests"` + WindowsIntegratedAuthentications float64 `perflib:"Windows Integrated Authentications"` +} + +func (c *adfsCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var adfsData []perflibADFS + err := unmarshalObject(ctx.perfObjects["AD FS"], &adfsData) + if err != nil { + return err + } + + for _, adfs := range adfsData { + ch <- prometheus.MustNewConstMetric( + c.adLoginConnectionFailures, + prometheus.CounterValue, + adfs.AdLoginConnectionFailures, + ) + + ch <- prometheus.MustNewConstMetric( + c.certificateAuthentications, + prometheus.CounterValue, + adfs.CertificateAuthentications, + ) + + ch <- prometheus.MustNewConstMetric( + c.deviceAuthentications, + prometheus.CounterValue, + adfs.DeviceAuthentications, + ) + + ch <- prometheus.MustNewConstMetric( + c.extranetAccountLockouts, + prometheus.CounterValue, + adfs.ExtranetAccountLockouts, + ) + + ch <- prometheus.MustNewConstMetric( + c.federatedAuthentications, + prometheus.CounterValue, + adfs.FederatedAuthentications, + ) + + ch <- prometheus.MustNewConstMetric( + c.passportAuthentications, + prometheus.CounterValue, + adfs.PassportAuthentications, + ) + + ch <- prometheus.MustNewConstMetric( + c.passiveRequests, + prometheus.CounterValue, + adfs.PassiveRequests, + ) + + ch <- prometheus.MustNewConstMetric( + c.passwordChangeFailed, + prometheus.CounterValue, + adfs.PasswordChangeFailed, + ) + + ch <- prometheus.MustNewConstMetric( + c.passwordChangeSucceeded, + prometheus.CounterValue, + adfs.PasswordChangeSucceeded, + ) + + ch <- prometheus.MustNewConstMetric( + c.tokenRequests, + prometheus.CounterValue, + adfs.TokenRequests, + ) + + ch <- prometheus.MustNewConstMetric( + c.windowsIntegratedAuthentications, + prometheus.CounterValue, + adfs.WindowsIntegratedAuthentications, + ) + } + return nil +} diff --git a/docs/README.md b/docs/README.md index 42a1d66d..07d3bce4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,6 +3,7 @@ This directory contains documentation of the collectors in the WMI exporter, wit # Collectors - [`ad`](collector.ad.md) +- [`adfs`](collector.adfs.md) - [`cpu`](collector.cpu.md) - [`cs`](collector.cs.md) - [`dns`](collector.dns.md) diff --git a/docs/collector.adfs.md b/docs/collector.adfs.md new file mode 100644 index 00000000..cd00dfd4 --- /dev/null +++ b/docs/collector.adfs.md @@ -0,0 +1,51 @@ +# adfs collector + +The adfs collector exposes metrics about Active Directory Federation Services. Note that this collector has only been tested against ADFS 4.0 (2016). +Other ADFS versions may work but are not tested. + +||| +-|- +Metric name prefix | `adfs` +Data source | Perflib +Counters | `AD FS` +Enabled by default? | No + +## Flags + +None + +## Metrics + +Name | Description | Type | Labels +-----|-------------|------|------- +`wmi_adfs_ad_login_connection_failures` | Total number of connection failures between the ADFS server and the Active Directory domain controller(s) | counter | None +`wmi_adfs_certificate_authentications` | Total number of [User Certificate](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/configure-user-certificate-authentication) authentications. I.E. smart cards or mobile devices with provisioned client certificates | counter | None +`wmi_adfs_device_authentications` | Total number of [device authentications](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/device-authentication-controls-in-ad-fs) (SignedToken, clientTLS, PkeyAuth). Device authentication is only available on ADFS 2016 or later | counter | None +`wmi_adfs_extranet_account_lockouts` | Total number of [extranet lockouts](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/configure-ad-fs-extranet-smart-lockout-protection). Requires the Extranet Lockout feature to be enabled | counter | None +`wmi_adfs_federated_authentications` | Total number of authentications from federated sources. E.G. Office365 | counter | None +`wmi_adfs_passport_authentications` | Total number of authentications from [Microsoft Passport](https://en.wikipedia.org/wiki/Microsoft_account) (now named Microsoft Account) | counter | None +`wmi_adfs_password_change_failed` | Total number of failed password changes. The Password Change Portal must be enabled in the AD FS Management tool in order to allow user password changes | counter | None +`wmi_adfs_password_change_succeeded` | Total number of succeeded password changes. The Password Change Portal must be enabled in the AD FS Management tool in order to allow user password changes | counter | None +`wmi_adfs_token_requests` | Total number of requested access tokens | counter | None +`wmi_adfs_windows_integrated_authentications` | Total number of Windows integrated authentications using Kerberos or NTLM | counter | None + +### Example metric +Show rate of device authentications in AD FS: +``` +rate(wmi_adfs_device_authentications)[2m] +``` + +## Useful queries + +## Alerting examples +**prometheus.rules** +```yaml + - alert: "HighExtranetLockouts" + expr: "rate(wmi_adfs_extranet_account_lockouts)[2m] > 100" + for: "10m" + labels: + severity: "high" + annotations: + summary: "High number of AD FS extranet lockouts" + description: "High number of AD FS extranet lockouts may indicate a password spray attack.\n Server: {{ $labels.instance }}\n Number of lockouts: {{ $value }}" +```