-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathcomparison.html
498 lines (474 loc) · 32 KB
/
comparison.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
<!DOCTYPE html>
<html lang="en" style="height: 100%">
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-93621L5G27"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-93621L5G27');
</script>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, shrink-to-fit=no" name="viewport">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Athenz IO - Explore</title>
<meta property="og:title" content="Open source platform for X.509 certificate based service authentication and fine grained access control in dynamic infrastructures">
<meta property="og:description" content="Open source platform for X.509 certificate based service authentication and fine grained access control in dynamic infrastructures">
<meta property="og:type" content="website"
><meta property="og:url" content="https://athenz.io/">
<meta property="og:site_name" content="Athenz">
<meta itemprop=name content="Open source platform for X.509 certificate based service authentication and fine grained access control in dynamic infrastructures">
<meta itemprop=description content="Open source platform for X.509 certificate based service authentication and fine grained access control in dynamic infrastructures">
<meta name=twitter:card content="summary">
<meta name=twitter:title content="Open source platform for X.509 certificate based service authentication and fine grained access control in dynamic infrastructures">
<meta name=twitter:description content="Open source platform for X.509 certificate based service authentication and fine grained access control in dynamic infrastructures">
<meta name=twitter:image content="https://athenz.io/images/icons/athenz-twitter-icon.jpg">
<meta name=twitter:image:alt content="Athenz">
<meta property="og:image" content="/images/icons/athenz-icon.png">
<link rel="icon" href="images/icons/favicon.ico"/>
<link href="https://cdn.jsdelivr.net/gh/denali-design/[email protected]/css/denali.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/gh/denali-design/[email protected]/dist/denali-icon-font.css" rel="stylesheet">
<link href="css/site.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;700;900&display=swap" rel="stylesheet">
</head>
<body class="denali-default-theme">
<div class="nav rubik-regular">
<div class="nav-left">
<a onclick="toggleTabsLeft()" tabindex="0" id="toggleTab" class="nav-icon hide-small-desktop-up-custom">
<i class="d-icon d-menu-hamburger"></i>
</a>
<a href="/">
<img class="nav-brand" src="images/icons/athenz-icon.png" alt="Athenz logo" />
</a>
<div class="float-right hide-small-desktop-up-custom">
<a class="nav-icon" id="navToggle"><i class="d-icon d-more-vertical"></i></a>
</div>
</div>
<div class="nav-responsive" id="navMenuContent">
<div class="nav-center">
<a class="nav-item" href="explore.html#model">Explore</a>
<a class="nav-item" href="casestudies.html#hybrid">Case Studies</a>
<a class="nav-item" href="https://athenz.github.io/athenz/" target="_blank">Documentation <i class="d-icon d-external is-sub is-extrasmall"></i></a>
<a class="nav-item" href="comparison.html#general">Comparison</a>
</div>
<div class="nav-right">
<a href="https://join.slack.com/t/athenz/shared_invite/zt-6c13sdfn-Lilbb~v2FYOkM7hZLGqF6A" target="_blank" class="nav-icon">
<i class="d-icon d-slack"></i>
<span class="icon-name">Slack</span>
</a>
<a href="https://github.com/AthenZ/athenz" target="_blank" class="nav-icon">
<i class="d-icon d-github"></i>
<span class="icon-name">Github</span>
</a>
<a href="contact.html" class="nav-icon">
<i class="d-icon d-help-circle"></i>
<span class="icon-name">Contact</span>
</a>
</div>
</div>
</div>
<div class="flex h-minus-nav" >
<div id="toggleTabsLeft" class="tabs is-primary is-vertical tablet-down-hide-left-custom">
<ul>
<li><a onclick="toggleTabActive('general')" class="is-active tablinks general" href="#general">General</a></li>
<li><a onclick="toggleTabActive('management')" class="tablinks management" href="#management">Management</a></li>
<li><a onclick="toggleTabActive('authn-support')" class="tablinks authn-support" href="#authn-support">Service Identity Support</a></li>
<li><a onclick="toggleTabActive('authz-support')" class="tablinks authz-support" href="#authz-support">Authorization Support</a></li>
<li><a onclick="toggleTabActive('extensibility')" class="tablinks extensibility" href="#extensibility">Extensibility</a></li>
</ul>
</div>
<div id="container" class="overflow-y-auto flex-auto comparison-container">
<div class="maincontent">
<!-- General -->
<div id="general" class="tabcontent" >
<div class="comparison-banner">
<div class="rubik-bold b-title">Athenz vs. SPIRE Comparison</div>
</div>
<div class="container-full p-t-30">
<p class="rubik-bold f-s-28">General</p>
<div class="comparison-content general">
<table class="comparison-table">
<thead>
<tr>
<th></th>
<th class="athenz-title"><div class="th-container"><img class="comparison-athenz-logo" src="images/icons/favicon.ico" alt="Athenz logo"/> Athenz</div></th>
<th class="spire-title"><div class="th-container"><img class="comparison-spire-logo" src="images/icons/spire-icon-color.png" alt="Spire logo"/> SPIRE</div></th>
</tr>
</thead>
<tbody>
<tr>
<td><div>License</div></td>
<td><div>Apache v2</div></td>
<td><div>Apache v2</div></td>
</tr>
<tr>
<td><div>Components</div></td>
<td>
<div>Management Server (Java)</div>
<div>Token Server (Java)</div>
<div>UI (Node.js)</div>
</td>
<td><div>Server (Go)</div></td>
</tr>
<tr>
<td><div>User Community</div></td>
<td><div>Small</div></td>
<td><div>Big</div></td>
</tr>
</tbody>
</table>
<div class="athenz-overlay-box overlay-box"></div>
<p class="p-t-30 rubik-bold f-s-20">License</p>
<p class="rubik-regular">Both technologies are available under fully open source licenses.</p>
<p class="p-t-30 rubik-bold f-s-20">Components</p>
<p class="rubik-regular">
Athenz includes 3 major components: Management Server, Token Server and UI. The servers are written in Java
using Jetty 9.4.x. The servers provide a REST interface with clients libraries available in Java and Go. The Athenz UI is a
Node.Js 12.x application. The service identity agents and various utilities are all Go applications. The major component in Spire
is the server written in Go. The identity agents are also Go applications. Client libraries are available in Go, Java and C++ languages.
</p>
<p class="p-t-30 rubik-bold f-s-20">User Community</p>
<p class="rubik-regular">
Spire has a big user community and is hosted by the Cloud Native Computing Foundation (CNCF) as an incubation-level
project. While Athenz powers most of workloads deployed within Yahoo with identity certificates, it is just starting to
focus on building its user community by joining CNCF and applying as a sandbox-level project.
</p>
</div>
</div>
</div>
<!-- Management -->
<div id="management" class="tabcontent" >
<div class="comparison-banner">
<div class="rubik-bold b-title">Athenz vs. SPIRE Comparison</div>
</div>
<div class="container-full p-t-30">
<p class="rubik-bold f-s-28">Management</p>
<div class="comparison-content management">
<table class="comparison-table">
<thead>
<tr>
<th></th>
<th class="athenz-title"><div class="th-container"><img class="comparison-athenz-logo" src="images/icons/favicon.ico" alt="Athenz logo"/> Athenz</div></th>
<th class="spire-title"><div class="th-container"><img class="comparison-spire-logo" src="images/icons/spire-icon-color.png" alt="Spire logo"/> SPIRE</div></th>
</tr>
</thead>
<tbody>
<tr>
<td><div>Delegated Management Support</div></td>
<td><div>Yes</div></td>
<td><div>No</div></td>
</tr>
<tr>
<td><div>Full Featured Self Serve UI</div></td>
<td><div>Yes</div></td>
<td><div>No</div></td>
</tr>
<tr>
<td><div>Tenancy Support</div></td>
<td><div>Yes</div></td>
<td><div>Multiple instances through federation</div></td>
</tr>
</tbody>
</table>
<div class="athenz-overlay-box overlay-box"></div>
<p class="p-t-30 rubik-bold f-s-20">Delegated Management Support</p>
<p class="rubik-regular">
In Spire setup a single instance is associated with a unique trust domain and administrator invokes commands against the server directly.
</p>
<p class="rubik-regular">
Athenz, on other hand, has the concept of User Authorities where you integrate with your existing User Authentication System such as Okta,
LDAP or even Unix users. The system administrator then creates a top level domain for each product in the same instance and assigns
administrator access for those domains to individual users or groups. For example, sports and mail products have their own completely
separate domains with their own administrators. If sports and mail administrators want to register their own services, the server
authenticates and authorizes those requests and allows them to complete those requests. Furthermore, each product domain can create
their own subdomains with further delegated management. For example, the sports domain administrator can create 3 separate sub-domains:
sports.prod, sports.stage, and sports.dev where sports.prod only allows their production engineers access while sports.stage and
sports.dev have developers access to those domains.
</p>
<p class="p-t-30 rubik-bold f-s-20">Full Featured Self Serve UI</p>
<p class="rubik-regular">
Athenz provides a full features UI (a Node.js application) where domain administrators can login and create roles, groups, policies and
services for their domains. It also provides the capability to register services and authorize which providers can launch them
(e.g. sports may deploy their api service in Kubernetes), so it will authorize Kubernetes agent to launch any instance with api service,
while they might also deploy their backend service in AWS thus only authorize AWS agent to deploy instances with backend service.
The UI also provides easy access to review and confirm/reject any role member access operations to satisfy Governance, Risk and Compliance
requirements if required.
</p>
<p class="p-t-30 rubik-bold f-s-20">Tenancy Support</p>
<p class="rubik-regular">
To support a single organization with different products and administrations, Spire requires multiple instances to be deployed and managed
and setup federation between those instances in order to provide interoperability between services of those products.
Athenz only provides a single deployment where each product is assigned its own top level/trust domain with their own set of administrators
(as described in the Delegated Management Support above). Having a single deployment allows a single team within the organization to manage
and secure that instance as opposed to managing and maintaining multiple independent instances.
</p>
</div>
</div>
</div>
<!-- Authn Support -->
<div id="authn-support" class="tabcontent">
<div class="comparison-banner">
<div class="rubik-bold b-title">Athenz vs. SPIRE Comparison</div>
</div>
<div class="container-full p-t-30">
<p class="rubik-bold f-s-28">Service Identity Support</p>
<div class="comparison-content authn-support">
<table class="comparison-table">
<thead>
<tr>
<th></th>
<th class="athenz-title"><div class="th-container"><img class="comparison-athenz-logo" src="images/icons/favicon.ico" alt="Athenz logo"/> Athenz</div></th>
<th class="spire-title"><div class="th-container"><img class="comparison-spire-logo" src="images/icons/spire-icon-color.png" alt="Spire logo"/> SPIRE</div></th>
</tr>
</thead>
<tbody>
<tr>
<td><div>Full SPIFFE Standard Implementation</div></td>
<td><div>Support for SPIFFE ID URIs</div><div> only in X.509 Certs</div></td>
<td><div>Yes</div></td>
</tr>
<tr>
<td><div>Issue both identity JWT Tokens</div><div> and X.509 certificates</div></td>
<td><div>X.509 Certificates</div></td>
<td><div>Yes</div></td>
</tr>
<tr>
<td><div>Kubernetes Support</div></td>
<td><div>Yes</div></td>
<td><div>Yes</div></td>
</tr>
<tr>
<td><div>AWS Support</div></td>
<td><div>EC2, ECS, EKS, Fargate, Lambda</div></td>
<td><div>EC2</div></td>
</tr>
<tr>
<td><div>Azure Support</div></td>
<td><div>Yes</div></td>
<td><div>Yes</div></td>
</tr>
<tr>
<td><div>GCP Support</div></td>
<td><div>No</div></td>
<td><div>Yes</div></td>
</tr>
</tbody>
</table>
<div class="athenz-overlay-box overlay-box"></div>
<p class="p-t-30 rubik-bold f-s-20">SPIFFE Standard Implementation</p>
<p class="rubik-regular">
The SPIFFE standard defines how services identity themselves using IDs implemented as Uniform Resource Identifiers (URIs),
how they're included in SPIFFE Verifiable Identity Document (SVIDs) such as X.509 Certificates and an API specification for issuing
and/or retrieving SVIDs known as the Workload API. The Spire is the reference implementation of SPIFFE specifications thus supports
the above listed items. Athenz supports issuing X.509 Certificates to registered services with SPIFEE IDs as URIs, however it does not
implement the Workload APIs. Athenz provides its own REST API that agents use to retrieve certificates and provide them to services as
PEM encoded files on disk.
</p>
<p class="p-t-30 rubik-bold f-s-20">Issue both identity JWT Tokens and X.509 Certificates</p>
<p class="rubik-regular">
Spire provides SPIFEE IDs in both X.509 Certificates and JWT Tokens for services to identify themselves.
Athenz only issues X.509 Certificates which allows us to enforce that all services within the organization use mTLS for service to
service communication.
</p>
<p class="p-t-30 rubik-bold f-s-20">Kubernetes Support</p>
<p class="rubik-regular">
Both Athenz and Spire provide support for issuing identity to workloads deployed within Kubernetes clusters. The Kubernetes team at
Yahoo has open-sourced several components that integrate Kubernetes with Athenz:
<ul class="rubik-regular">
<li><a href="https://github.com/yahoo/k8s-athenz-identity">k8s-athenz-identity</a> is a Kubernetes control plane component which aims
to securely provision unique Athenz identities (X.509 certificates) for pods.</li>
<li><a href="https://github.com/yahoo/k8s-athenz-webhook">k8s-athenz-webhook</a> is an API for a Kubernetes authentication and
authorization webhook that integrates with Athenz for access checks. It allows flexible resource mapping from Kubernetes resources
to Athenz.</li>
<li><a href="https://github.com/yahoo/k8s-athenz-istio-auth">k8s-athenz-istio-auth</a> is a controller which converts Athenz domains to
Istio RBAC custom resources.</li>
</ul>
</p>
<p class="p-t-30 rubik-bold f-s-20">AWS Support</p>
<p class="rubik-regular">
Both Athenz and Spire provide support for issuing identity to workloads deployed within AWS EC2. Spire relies on AWS metadata documents
for attestation while Athenz requires both metadata document and AWS Temporary credentials (with empty IAM roles that do not expose access
to any resources). The attestation in Athenz is based on the AWS account registered with the trust domain thus allowing any instance that
is bootstrapped as part of Auto Scaling Group auto-scale deployments to obtain service identities without explicitly registering the EC2
instances. Additionally, based on AWS temporary credentials support, Athenz supports issuing X.509 certificates to Fargate and Lambda
workloads in AWS. There are efforts in the Spire community to add support for serverless workloads in AWS.
</p>
<p class="p-t-30 rubik-bold f-s-20">Azure Support</p>
<p class="rubik-regular">
Both Athenz and Spire provide support for issuing identity to workloads deployed within Azure Virtual Machines. Both seem to rely on the
Azure JWT Tokens that VMs would obtain based on their managed service identities and present for attestation to their respective servers.
</p>
<p class="p-t-30 rubik-bold f-s-20">GCP Support</p>
<p class="rubik-regular">
Spire issues identities to workloads deployed in GCP based on the GCP Instance Identity Tokens. Athenz currently does not have an agent
supporting GCP workloads.
</p>
</div>
</div>
</div>
<!-- Authz Support -->
<div id="authz-support" class="tabcontent" >
<div class="comparison-banner">
<div class="rubik-bold b-title">Athenz vs. SPIRE Comparison</div>
</div>
<div class="container-full p-t-30">
<p class="rubik-bold f-s-28">Authorization Support</p>
<div class="comparison-content authz-support">
<table class="comparison-table">
<thead>
<tr>
<th></th>
<th class="athenz-title"><div class="th-container"><img class="comparison-athenz-logo" src="images/icons/favicon.ico" alt="Athenz logo"/> Athenz</div></th>
<th class="spire-title"><div class="th-container"><img class="comparison-spire-logo" src="images/icons/spire-icon-color.png" alt="Spire logo"/> SPIRE</div></th>
</tr>
</thead>
<tbody>
<tr>
<td><div>RBAC Authorization System</div><div> with JWT Access Tokens</div></td>
<td><div>Yes</div></td>
<td><div>No</div></td>
</tr>
<tr>
<td><div>mTLS Bound JWT</div><div> Access Token Support</div></td>
<td><div>Yes</div></td>
<td><div>N/A</div></td>
</tr>
</tbody>
</table>
<div class="athenz-overlay-box overlay-box"></div>
<p class="p-t-30 rubik-bold f-s-20">RBAC Authorization System with JWT Access Tokens</p>
<p class="rubik-regular">Athenz provides fine-grained role-based access control (RBAC) support for a centralized management system with
support for control-plane access control decisions and a decentralized enforcement mechanism suitable for data-plane access control
decisions. It issues industry standard JWT Access Token to all registered services that clients present to their respective resource
servers to enforce both authentication (based on their x.509 client certificates) and authorization (based on their access tokens).</p>
<p class="p-t-30 rubik-bold f-s-20">mTLS Bound JWT Access Token Support</p>
<p class="rubik-regular">
Athenz Token Service issues industry standard mTLS bound OAuth2 access tokens that application services can use to both authenticate
(x.509 identity certificates) and authorize requests based on policies defined in the Athenz Management System. With the mTLS binding,
the replay attacks are only possible if both the access token and the private key of the service have been compromised.
</p>
</div>
</div>
</div>
<!-- Extensibility -->
<div id="extensibility" class="tabcontent" >
<div class="comparison-banner">
<div class="rubik-bold b-title">Athenz vs. SPIRE Comparison</div>
</div>
<div class="container-full p-t-30">
<p class="rubik-bold f-s-28">Extensibility</p>
<div class="comparison-content extensibility">
<table class="comparison-table">
<thead>
<tr>
<th></th>
<th class="athenz-title"><div class="th-container"><img class="comparison-athenz-logo" src="images/icons/favicon.ico" alt="Athenz logo"/> Athenz</div></th>
<th class="spire-title"><div class="th-container"><img class="comparison-spire-logo" src="images/icons/spire-icon-color.png" alt="Spire logo"/> SPIRE</div></th>
</tr>
</thead>
<tbody>
<tr>
<td><div>Pluggable DataStore Plugin</div></td>
<td><div>Yes (default using MySQL)</div></td>
<td><div>Yes (default using sqlite3)</div></td>
</tr>
<tr>
<td><div>Key Store Plugin</div></td>
<td><div>Yes</div></td>
<td><div>Yes</div></td>
</tr>
</tbody>
</table>
<div class="athenz-overlay-box overlay-box"></div>
<p class="p-t-30 rubik-bold f-s-20">Pluggable DataStore Plugin</p>
<p class="rubik-regular">
Both Athenz and Spire are extensible when it comes to providing plugin capabilities to store required data.
Spire is using sqlite3 by default, while Athenz provides a datastore plugin for MySQL servers (using AWS Aurora for AWS deployments).
</p>
<p class="p-t-30 rubik-bold f-s-20">Key Store Plugin</p>
<p class="rubik-regular">
Both Athenz and Spire are extensible when it comes to providing plugin capabilities to store and sign certificates.
Athenz also provides support for <a href="https://github.com/theparanoids/crypki">crypki</a> which is open-sourced by the Yahoo
Paranoids Team and is a simple service for interacting with an HSM or other PKCS #11 device. It supports minting and signing of both
SSH and x509 certificates.
</p>
</div>
</div>
</div>
</div>
<!--footer-->
<footer class="container-full custom-footer">
<div class="container">
<div class="row p-t-70">
<div class="xs-col-4-12 col-2-12 p-b-30">
<ul>
<li class="rubik-bold f-s-16">Community</li>
<li class="rubik-regular"><a href="https://join.slack.com/t/athenz/shared_invite/zt-6c13sdfn-Lilbb~v2FYOkM7hZLGqF6A" target="_blank">Slack</a></li>
<li class="rubik-regular"><a href="contact.html">Contact Us</a></li>
</ul>
</div>
<div class="xs-col-4-12 col-2-12 p-b-30">
<ul>
<li class="rubik-bold f-s-16">Contribute</li>
<li class="rubik-regular"><a href="https://github.com/AthenZ/athenz" target="_blank">Github</a></li>
<li class="rubik-regular"><a href="https://github.com/AthenZ/athenz/issues" target="_blank">Suggest</a></li>
</ul>
</div>
<div class="xs-col-4-12 col-2-12 p-b-30">
<ul>
<li class="rubik-bold f-s-16">Resources</li>
<li class="rubik-regular"><a href="https://athenz.github.io/athenz/" target="_blank">Documentation</a></li>
</ul>
</div>
</div>
<div class="nav-center">
Copyright © 2022 The Athenz Authors<br>
Copyright © 2022 The Linux Foundation ®. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks.
For a list of trademarks of The Linux Foundation, please see our <a href="https://www.linuxfoundation.org/trademark-usage" target="_blank">Trademark Usage page</a>
</div>
</div>
</footer>
</div>
</div>
</body>
<script type="text/javascript">
function toggleTabsLeft() {
let toggleTabsLeft = document.getElementById("toggleTabsLeft");
toggleTabsLeft.classList.toggle('tablet-down-toggle-tabs-left-custom');
};
document.getElementById('navToggle').addEventListener("click", toggleMenu);
function toggleMenu() {
document.getElementById('navMenuContent').classList.toggle("is-active");
}
function toggleTabActive(tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace("is-active", "");
}
document.getElementById(tabName).style.display = "block";
var activeTab = document.getElementsByClassName(tabName);
activeTab[0].classList.add("is-active");
let toggleTabsLeft = document.getElementById("toggleTabsLeft");
if(toggleTabsLeft.classList.contains("tablet-down-toggle-tabs-left-custom")) {
toggleTabsLeft.classList.toggle('tablet-down-toggle-tabs-left-custom');
}
}
document.addEventListener("DOMContentLoaded", function(){
// Handler when the DOM is fully loaded
function onHashChange() {
var hash = window.location.hash;
if (hash) {
toggleTabActive(hash.split('#')[1]);
}
}
window.addEventListener('hashchange', onHashChange, false);
onHashChange();
});
</script>
</html>