0

List active sessions

by
Published today

List active APM access sessions (session id, logon user, client IP), optionally filtered by exact logon user or client IP. Paged by the script with limit (default 100) and offset; returns total and next_offset.

Script f5_bigip Verified

The script

Submitted by hugo989 Typescript (fetch-only)
Verified 6 hours ago
1
//native
2

3
async function connect(auth: RT.F5Bigip): Promise<{
4
  base: string
5
  headers: { [key: string]: string }
6
  release: () => Promise<void>
7
}> {
8
  const base = auth.base_url.replace(/\/+$/, "")
9
  if (auth.auth_method === "basic") {
10
    return {
11
      base,
12
      headers: {
13
        Authorization: `Basic ${btoa(`${auth.username}:${auth.password}`)}`,
14
      },
15
      release: async () => {},
16
    }
17
  }
18
  const login = await fetch(`${base}/mgmt/shared/authn/login`, {
19
    method: "POST",
20
    headers: { "Content-Type": "application/json", Accept: "application/json" },
21
    body: JSON.stringify({
22
      username: auth.username,
23
      password: auth.password,
24
      loginProviderName: auth.login_provider_name || "tmos",
25
    }),
26
  })
27
  if (!login.ok) {
28
    throw new Error(`${login.status} ${await login.text()}`)
29
  }
30
  const token: string = (await login.json()).token.token
31
  return {
32
    base,
33
    headers: { "X-F5-Auth-Token": token },
34
    release: async () => {
35
      await fetch(
36
        `${base}/mgmt/shared/authz/tokens/${encodeURIComponent(token)}`,
37
        {
38
          method: "DELETE",
39
          headers: { "X-F5-Auth-Token": token },
40
        }
41
      )
42
    },
43
  }
44
}
45

46
/**
47
 * List active sessions
48
 * List active APM access sessions (session id, logon user, client IP) from /mgmt/tm/apm/access-info. BIG-IP returns every session in one stats document; filtering by user or client IP and the limit/offset window are applied by the script.
49
 */
50
export async function main(
51
  auth: RT.F5Bigip,
52
  logon_user: string | undefined,
53
  client_ip: string | undefined,
54
  limit: number | undefined,
55
  offset: number | undefined
56
) {
57
  const f5 = await connect(auth)
58
  try {
59
    const response = await fetch(`${f5.base}/mgmt/tm/apm/access-info`, {
60
      headers: { ...f5.headers, Accept: "application/json" },
61
    })
62

63
    if (!response.ok) {
64
      throw new Error(`${response.status} ${await response.text()}`)
65
    }
66

67
    // entries: { "https://localhost/mgmt/tm/apm/access-info/<session id>": { nestedStats: { entries: { clientIp: { description }, logonUser: { description } } } } }
68
    const stats = await response.json()
69
    const all = Object.entries(stats.entries ?? {}).map(
70
      ([link, value]: [string, any]) => {
71
        const fields = value?.nestedStats?.entries ?? {}
72
        return {
73
          session_id: new URL(link).pathname.split("/").pop() as string,
74
          logon_user: fields.logonUser?.description ?? null,
75
          client_ip: fields.clientIp?.description ?? null,
76
        }
77
      }
78
    )
79
    // Older releases list an empty placeholder entry (bug 713389); keep real sessions only.
80
    const sessions = all.filter(
81
      (s) =>
82
        (s.logon_user || s.client_ip) &&
83
        (!logon_user || s.logon_user === logon_user) &&
84
        (!client_ip || s.client_ip === client_ip)
85
    )
86

87
    const start = offset || 0
88
    const end = start + (limit || 100)
89
    return {
90
      total: sessions.length,
91
      sessions: sessions.slice(start, end),
92
      next_offset: end < sessions.length ? end : null,
93
    }
94
  } finally {
95
    await f5.release()
96
  }
97
}
98