• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pomerium / pomerium / 20077319089

09 Dec 2025 08:20PM UTC coverage: 52.794% (-0.004%) from 52.798%
20077319089

push

github

web-flow
feat(ssh): add auth code flow telemetry signals (#5973)

## Summary

Adds useful sub-spans, structured logs, and metrics for ssh auth code
flow.

## Related issues


[ENG-3134](https://linear.app/pomerium/issue/ENG-3134/ssh-auth-code-flow-telemetry-logs-metrics-traces)
Indirectly related to
[ENG-3058](https://linear.app/pomerium/issue/ENG-3058/ssh-rate-limit-incoming-pending-sessions)

## User Explanation

There is now structured telemetry around SSH authorization code flow.

## Checklist

- [X] reference any related issues
- [x] updated unit tests
- [X] add appropriate label (`enhancement`, `bug`, `breaking`,
`dependencies`, `ci`)
- [x] ready for review

96 of 177 new or added lines in 3 files covered. (54.24%)

11 existing lines in 3 files now uncovered.

29153 of 55220 relevant lines covered (52.79%)

84.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pkg/ssh/code/metrics.go
1
package code
2

3
import (
4
        "context"
5

6
        otelattribute "go.opentelemetry.io/otel/attribute"
7
        "go.opentelemetry.io/otel/metric"
8
)
9

NEW
10
func linearBuckets(numBuckets int, maximum float64) []float64 {
×
NEW
11
        if numBuckets <= 0 || maximum <= 0 {
×
NEW
12
                return nil
×
NEW
13
        }
×
NEW
14
        buckets := make([]float64, numBuckets)
×
NEW
15
        step := maximum / float64(numBuckets)
×
NEW
16
        for i := range numBuckets {
×
NEW
17
                buckets[i] = step * float64(i+1)
×
NEW
18
        }
×
NEW
19
        return buckets
×
20
}
21

22
type Metrics struct {
23
        // SSHAuthCodeRequestsTotal tracks the total number of times an authentication request comes into the ssh endpoint
24
        SSHAuthCodeRequestsTotal metric.Int64Counter
25
        // SSHAuthCodeRequestsTotal counts the total number of ssh clients connected and awaiting authentication
26
        // this can be for three main reasons
27
        // - internal : an internal bug or unexpected behaviour
28
        // - user-revoked : user denied the code
29
        // - timeout : user failed to authenticate within the timeout, or cancelled the request
30
        SSHAuthCodeRequestFailuresTotal metric.Int64Counter
31
        // SSHActivePendingSessions counts the total number of ssh clients connected and awaiting authentication
32
        SSHActivePendingSessions metric.Int64Counter
33
        // SSHIssueCodeDuration measures the duration it takes to associated a unique code to a request in the ssh authorization code flow
34
        SSHIssueCodeDuration metric.Float64Histogram
35
        // SSHUserCodeDecisionDuration measures the duration from the time the code is issued to when it is accepted or denied
36
        SSHUserCodeDecisionDuration metric.Float64Histogram
37
}
38

NEW
39
func NewMetrics(meter metric.Meter) (*Metrics, error) {
×
NEW
40
        codeReqTotal, err := meter.Int64Counter(
×
NEW
41
                "ssh.auth.code.requests.total",
×
NEW
42
                metric.WithDescription("tracks the total number of times an authentication request comes into the ssh endpoint"),
×
NEW
43
        )
×
NEW
44
        if err != nil {
×
NEW
45
                return nil, err
×
NEW
46
        }
×
NEW
47
        codeRequestFailuresTotal, err := meter.Int64Counter(
×
NEW
48
                "ssh.auth.code.requests.failures.total",
×
NEW
49
                metric.WithDescription("tracks the total number of failures to authenticate a code request."),
×
NEW
50
        )
×
NEW
51
        if err != nil {
×
NEW
52
                return nil, err
×
NEW
53
        }
×
54

NEW
55
        activePendingSessions, err := meter.Int64Counter(
×
NEW
56
                "ssh.auth.code.pending.sessions.count",
×
NEW
57
                metric.WithDescription("counts the total number of ssh clients connected and awaiting authentication"),
×
NEW
58
        )
×
NEW
59
        if err != nil {
×
NEW
60
                return nil, err
×
NEW
61
        }
×
NEW
62
        issueCodeDuration, err := meter.Float64Histogram(
×
NEW
63
                "ssh.auth.code.issue.duration",
×
NEW
64
                metric.WithUnit("s"),
×
NEW
65
                metric.WithDescription("measures the duration it takes to associated a unique code to a request in the ssh authorization code flow"),
×
NEW
66
        )
×
NEW
67
        if err != nil {
×
NEW
68
                return nil, err
×
NEW
69
        }
×
70

NEW
71
        userCodeDecision, err := meter.Float64Histogram(
×
NEW
72
                "ssh.auth.code.user.decision",
×
NEW
73
                metric.WithDescription("measures the duration from the time the code is issued to when it is accepted or denied"),
×
NEW
74
                metric.WithExplicitBucketBoundaries(
×
NEW
75
                        linearBuckets(10, DefaultCodeTTL.Seconds())...,
×
NEW
76
                ),
×
NEW
77
        )
×
NEW
78
        if err != nil {
×
NEW
79
                return nil, err
×
NEW
80
        }
×
NEW
81
        return &Metrics{
×
NEW
82
                SSHAuthCodeRequestsTotal:        codeReqTotal,
×
NEW
83
                SSHAuthCodeRequestFailuresTotal: codeRequestFailuresTotal,
×
NEW
84
                SSHActivePendingSessions:        activePendingSessions,
×
NEW
85
                SSHIssueCodeDuration:            issueCodeDuration,
×
NEW
86
                SSHUserCodeDecisionDuration:     userCodeDecision,
×
NEW
87
        }, nil
×
88
}
89

NEW
90
func (m *Metrics) PendingSessionInc(ctx context.Context) {
×
NEW
91
        m.SSHActivePendingSessions.Add(ctx, 1)
×
NEW
92
}
×
93

NEW
94
func (m *Metrics) PendingSessionDec(ctx context.Context) {
×
NEW
95
        m.SSHActivePendingSessions.Add(ctx, -1)
×
NEW
96
}
×
97

98
type Failure string
99

100
const (
101
        FailureTimeout  Failure = "timeout"
102
        FailureInternal Failure = "internal"
103
        FailureRevoked  Failure = "user-revoked"
104
)
105

106
func FailureReason(
107
        reason Failure,
NEW
108
) otelattribute.KeyValue {
×
NEW
109
        return otelattribute.String("failure", string(reason))
×
NEW
110
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc