Translated ['src/pentesting-cloud/kubernetes-security/kubernetes-pivotin

This commit is contained in:
Translator
2025-01-02 21:34:29 +00:00
parent 48580fee4d
commit fb47d86e90
7 changed files with 188 additions and 58 deletions

View File

@@ -22,6 +22,7 @@ after = ["links"]
[preprocessor.hacktricks]
command = "python3 ./hacktricks-preprocessor.py"
env = "prod"
[output.html]
additional-css = ["theme/pagetoc.css", "theme/tabs.css"]

View File

@@ -30,14 +30,16 @@ def ref(matchobj):
href = matchobj.groups(0)[0].strip()
title = href
if href.startswith("http://") or href.startswith("https://"):
# pass
try:
raw_html = str(urlopen(Request(href, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0'})).read())
match = re.search('<title>(.*?)</title>', raw_html)
title = match.group(1) if match else href
except Exception as e:
logger.debug(f'Error opening URL {href}: {e}')
pass #nDont stop on broken link
if context['config']['preprocessor']['hacktricks']['env'] == 'dev':
pass
else:
try:
raw_html = str(urlopen(Request(href, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0'})).read())
match = re.search('<title>(.*?)</title>', raw_html)
title = match.group(1) if match else href
except Exception as e:
logger.debug(f'Error opening URL {href}: {e}')
pass #nDont stop on broken link
else:
try:
if href.endswith("/"):

View File

@@ -8,10 +8,10 @@
### GCP-SA कुंजी को गुप्त के रूप में माउंट करना
**GCP के लिए एक कुबेरनेट्स एप्लिकेशन को पहुंच देने** का एक सामान्य तरीका है:
**GCP तक एक kubernetes एप्लिकेशन को पहुंच देने** का एक सामान्य तरीका है:
- एक GCP सेवा खाता बनाएं
- उस पर इच्छित अनुमतियों को बाइंड करें
- उस पर इच्छित अनुमतियों को बांधें
- बनाए गए SA की एक json कुंजी डाउनलोड करें
- इसे पॉड के अंदर एक गुप्त के रूप में माउंट करें
- GOOGLE_APPLICATION_CREDENTIALS पर्यावरण चर को उस पथ की ओर इंगित करें जहां json है।
@@ -21,9 +21,9 @@
### GSA json को KSA गुप्त से संबंधित करना
GKE क्लस्टर को GSA तक पहुंच देने का एक तरीका उन्हें इस तरह बाइंड करना है:
GKE क्लस्टर को GSA तक पहुंच देने का एक तरीका इस प्रकार है:
- निम्नलिखित कमांड का उपयोग करके अपने GKE क्लस्टर के समान नामस्थान में एक कुबेरनेट्स सेवा खाता बनाएं:
- निम्नलिखित कमांड का उपयोग करके अपने GKE क्लस्टर के समान नामस्थान में एक Kubernetes सेवा खाता बनाएं:
```bash
Copy codekubectl create serviceaccount <service-account-name>
```
@@ -40,13 +40,13 @@ Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
```
> [!WARNING]
> **दूसरे चरण** में **KSA के रहस्य के रूप में GSA के प्रमाण पत्र सेट किए गए थे**। फिर, यदि आप **GKE** क्लस्टर के **अंदर** से **उस रहस्य को पढ़ सकते हैं**, तो आप **उस GCP सेवा खाते** तक **उन्नति** कर सकते हैं।
> **दूसरे चरण** में **KSA के रहस्य के रूप में GSA के प्रमाणपत्र सेट किए गए थे**। फिर, यदि आप **GKE** क्लस्टर के **अंदर** से **उस रहस्य को पढ़ सकते हैं**, तो आप **उस GCP सेवा खाते** तक **उन्नति** कर सकते हैं।
### GKE कार्यभार पहचान
कार्यभार पहचान के साथ, हम एक[ Kubernetes सेवा खाता](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) को[ Google सेवा खाता](https://cloud.google.com/iam/docs/understanding-service-accounts) के रूप में कार्य करने के लिए कॉन्फ़िगर कर सकते हैं। Kubernetes सेवा खाते के साथ चलने वाले पॉड Google क्लाउड APIs तक पहुँचते समय स्वचालित रूप से Google सेवा खाते के रूप में प्रमाणित होंगे।
इस व्यवहार को सक्षम करने के लिए **पहली श्रृंखला के चरण** हैं **GCP में कार्यभार पहचान सक्षम करना** ([**चरण**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) और वह GCP SA बनाना जिसे आप k8s के रूप में कार्य करना चाहते हैं।
इस व्यवहार को सक्षम करने के लिए **पहली श्रृंखला के चरण** हैं **GCP में कार्यभार पहचान को सक्षम करना** ([**चरण**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) और वह GCP SA बनाना जिसे आप k8s के रूप में कार्य करना चाहते हैं।
- **नए क्लस्टर पर कार्यभार पहचान सक्षम करें**
```bash
@@ -59,7 +59,7 @@ gcloud container clusters update <cluster_name> \
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
```
- K8s से GCP अनुमतियों के साथ **GCP सेवा खाता बनाएं** जिसे अनुकरण किया जा सके:
- K8s से GCP अनुमतियों के साथ **GCP सेवा खाता बनाएं** जिसे अनुकरण करना है:
```bash
# Create SA called "gsa2ksa"
gcloud iam service-accounts create gsa2ksa --project=<project-id>
@@ -118,15 +118,15 @@ kubectl exec -it workload-identity-test \
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
gcloud auth list
```
इस कमांड को प्रमाणित करने के लिए जांचें यदि आवश्यक हो:
नीचे दिए गए कमांड को जांचें ताकि आवश्यकता होने पर प्रमाणीकरण किया जा सके:
```bash
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
```
> [!WARNING]
> एक हमलावर के रूप में K8s के अंदर आपको **SAs के लिए खोज करनी चाहिए** जिनके पास **`iam.gke.io/gcp-service-account` एनोटेशन** है क्योंकि यह संकेत करता है कि SA GCP में कुछ तक पहुँच सकता है। एक और विकल्प होगा कि क्लस्टर में प्रत्येक KSA का दुरुपयोग करने की कोशिश करें और जांचें कि क्या इसके पास पहुँच है।\
> GCP से हमेशा बाइंडिंग्स को सूचीबद्ध करना और जानना **कौन सा एक्सेस आप Kubernetes के अंदर SAs को दे रहे हैं** दिलचस्प होता है
> एक हमलावर के रूप में K8s के अंदर आपको **SAs के लिए खोज करनी चाहिए** जिनके पास **`iam.gke.io/gcp-service-account` एनोटेशन** है क्योंकि यह संकेत करता है कि SA GCP में कुछ तक पहुँच सकता है। एक और विकल्प होगा कि क्लस्टर में प्रत्येक KSA का दुरुपयोग करने की कोशिश करें और जांचें कि क्या इसक पहुँच है।\
> GCP से हमेशा बाइंडिंग को सूचीबद्ध करना और जानना **महत्वपूर्ण है कि आप Kubernetes के अंदर SAs को कौन सी पहुँच दे रहे हैं**।
यह एक स्क्रिप्ट है जो आसानी से **सभी पॉड्स** परिभाषाओं को **देखने** के लिए **उस एनोटेशन**ी खोज करती है:
यह एक स्क्रिप्ट है जो आसानी से **सभी पॉड्स** परिभाषाओं को **देखने** के लिए **इटरेट** करती है कि **एनोटेशन**े लिए:
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -171,7 +171,7 @@ annotations:
iam.amazonaws.com/role: reportingdb-reader
```
> [!WARNING]
> एक हमलावर के रूप में, यदि आप **इन एनोटेशन** को पॉड्स या नेमस्पेस में या एक kiam/kube2iam सर्वर में (संभवतः kube-system में) पाते हैं, तो आप **हर उस r**ोल का **नकली रूप धारण कर सकते हैं** जो पहले से **पॉड्स द्वारा उपयोग किया जा रहा है** और अधिक (यदि आपके पास AWS खाते तक पहुंच है तो भूमिकाओं की सूची बनाएं)।
> एक हमलावर के रूप में, यदि आप **इन एनोटेशन** को पॉड्स या नेमस्पेस में या एक kiam/kube2iam सर्वर (संभवतः kube-system में) चलाते हुए पाते हैं, तो आप **हर उस **ोल का **नकली रूप धारण कर सकते हैं** जो पहले से **पॉड्स द्वारा उपयोग किया जा रहा है** और अधिक (यदि आपके पास AWS खाते तक पहुंच है तो भूमिकाओं की सूची बनाएं)।
#### IAM भूमिका के साथ पॉड बनाएं
@@ -198,8 +198,8 @@ args: ["-c", "sleep 100000"]' | kubectl apply -f -
1. सबसे पहले आपको [क्लस्टर के लिए OIDC प्रदाता बनाना होगा](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html)।
2. फिर आप एक IAM भूमिका बनाते हैं जिसमें SA को आवश्यक अनुमतियाँ होती हैं।
3. एक [विश्वास संबंध बनाएं IAM भूमिका और SA के बीच](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) नाम (या उन namespaces को जो भूमिका को namespace के सभी SAs तक पहुँच प्रदान करते हैं)। _विश्वास संबंध मुख्य रूप से OIDC प्रदाता नाम, namespace नाम और SA नाम की जांच करेगा_।
4. अंत में, **एक SA बनाएं जिसमें भूमिका के ARN को इंगित करने वाला एक एनोटेशन हो**, और उस SA के साथ चलने वाले पॉड्स को **भूमिका के टोकन तक पहुँच प्राप्त होगी**। **टोकन** **एक फ़ाइल के अंदर लिखा गया है** और पथ **`AWS_WEB_IDENTITY_TOKEN_FILE`** में निर्दिष्ट है (डिफ़ॉल्ट: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
3. एक [विश्वास संबंध बनाएं IAM भूमिका और SA के बीच](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) नाम (या उन namespaces को जो भूमिका को namespace के सभी SAs तक पहुँच प्रदान करते हैं)। _विश्वास संबंध मुख्य रूप से OIDC प्रदाता का नाम, namespace का नाम और SA का नाम जांचेगा_।
4. अंत में, **एक SA बनाएं जिसमें भूमिका के ARN को इंगित करने वाला एक एनोटेशन हो**, और उस SA के साथ चलने वाले पॉड्स को **भूमिका के टोकन तक पहुँच** होगी। **टोकन** **एक फ़ाइल** के अंदर **लिखा** जाता है और पथ **`AWS_WEB_IDENTITY_TOKEN_FILE`** में निर्दिष्ट किया गया है (डिफ़ॉल्ट: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
```bash
# Create a service account with a role
cat >my-service-account.yaml <<EOF
@@ -221,14 +221,14 @@ kubectl annotate serviceaccount -n $namespace $service_account eks.amazonaws.com
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/EKSOIDCTesting --role-session-name something --web-identity-token file:///var/run/secrets/eks.amazonaws.com/serviceaccount/token
```
> [!WARNING]
> एक हमलावर के रूप में, यदि आप एक K8s क्लस्टर को सूचीबद्ध कर सकते हैं, तो **AWS** पर **उन्नति** करने के लिए **उस एनोटेशन** के साथ **सेवा खातों** की जांच करें। ऐसा करने के लिए, बस एक IAM **विशिष्ट सेवा खाते** का उपयोग करके **exec/create** एक **pod** करें और टोकन चुराएं।
> एक हमलावर के रूप में, यदि आप एक K8s क्लस्टर को सूचीबद्ध कर सकते हैं, तो **AWS** पर **उन्नति** करने के लिए **उस एनोटेशन के साथ सेवा खातों** की जांच करें। ऐसा करने के लिए, बस एक IAM **विशिष्ट सेवा खाते** का उपयोग करके **exec/create** एक **pod** करें और टोकन चुराएं।
>
> इसके अलावा, यदि आप एक pod के अंदर हैं, तो **AWS_ROLE_ARN** और **AWS_WEB_IDENTITY_TOKEN** जैसे env वेरिएबल्स की जांच करें।
> [!CAUTION]
> कभी-कभी एक भूमिका की **Turst Policy** **खराब तरीके से कॉन्फ़िगर** की जा सकती है और अपेक्षित सेवा खाते को AssumeRole एक्सेस देने के बजाय, यह **सभी सेवा खातों** को देती है। इसलिए, यदि आप एक नियंत्रित सेवा खाते पर एक एनोटेशन लिखने में सक्षम हैं, तो आप भूमिका तक पहुंच सकते हैं।
> कभी-कभी एक भूमिका की **Turst Policy** **खराब कॉन्फ़िगर** की जा सकती है और अपेक्षित सेवा खाते को AssumeRole एक्सेस देने के बजाय, यह **सभी सेवा खातों** को देती है। इसलिए, यदि आप एक नियंत्रित सेवा खाते पर एक एनोटेशन लिखने में सक्षम हैं, तो आप भूमिका तक पहुंच सकते हैं।
>
> अधिक जानकारी के लिए **निम्नलिखित पृष्ठ** की जांच करें:
> अधिक जानकारी के लिए **निम्नलिखित पृष्ठ की जांच करें**:
{{#ref}}
../aws-security/aws-basic-information/aws-federation-abuse.md
@@ -236,7 +236,7 @@ aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/
### Find Pods a SAs with IAM Roles in the Cluster
यह एक स्क्रिप्ट है जो आसानी से **सभी pods और sas** परिभाषाओं को **देखने** के लिए **इटरेट** करती है जो **उस एनोटेशन** की तलाश कर रही है:
यह एक स्क्रिप्ट है जो आसानी से **सभी pods और sas** परिभाषाओं को **देखने** के लिए **इटरेट** करती है जो **उस एनोटेशन** की तलाश करी है:
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -263,7 +263,7 @@ kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"s
```
### IAM भूमिका टोकन चुराना
पहले हमनें चर्चा की है कि **Pods को IAM भूमिकाएँ कैसे संलग्न करें** या यहां तक कि **IAM भूमिका चुराने के लिए नोड पर कैसे भागें** जो इंस्टेंस ने संलग्न की है।
पहले हमनें चर्चा की है कि **Pods को IAM भूमिकाएँ कैसे संलग्न करें** या यहां तक कि **IAM भूमिका चुराने के लिए नोड पर कैसे भागें** जो इंस्टेंस के साथ संलग्न है।
आप अपने नए मेहनत से कमाए गए **IAM भूमिका क्रेडेंशियल्स** को **चुराने** के लिए निम्नलिखित स्क्रिप्ट का उपयोग कर सकते हैं:
```bash

View File

@@ -590,6 +590,62 @@ function playground_text(playground, hidden = true) {
});
})();
(function menubarLanguage() {
var menubarLanguageToggleButton = document.getElementById('menubar-languages-toggle');
var menubarLanguagePopup = document.getElementById('menubar-languages-popup');
var languageButtons = menubarLanguagePopup.querySelectorAll('.menu-bar-link');
function showLanguage() {
menubarLanguagePopup.style.display = 'flex';
menubarLanguageToggleButton.setAttribute('aria-expanded', true);
}
function hideLanguage() {
menubarLanguagePopup.style.display = 'none';
menubarLanguageToggleButton.setAttribute('aria-expanded', false);
menubarLanguageToggleButton.focus();
}
menubarLanguageToggleButton.addEventListener('click', function () {
if (menubarLanguagePopup.style.display === 'flex') {
hideLanguage();
} else {
showLanguage();
}
});
menubarLanguagePopup.addEventListener('focusout', function(e) {
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
if (!!e.relatedTarget && !menubarLanguageToggleButton.contains(e.relatedTarget) && !menubarLanguagePopup.contains(e.relatedTarget)) {
hideLanguage();
}
});
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
document.addEventListener('click', function(e) {
if (menubarLanguagePopup.style.display === 'block' && !menubarLanguageToggleButton.contains(e.target) && !menubarLanguagePopup.contains(e.target)) {
hideLanguage();
}
});
languageButtons.forEach((btn) => {
btn.addEventListener('click', function(e) {
const regex = /(?:(?:\/)+(?<lang>[a-z]{2}(?=\/|$)))?(?<path>(?:\/)*.*)?/g
var match = regex.exec(window.location.pathname)
var path = match.groups.path
console.log(`Path: ${path} ${typeof path}`)
const lang = match.groups.lang
console.log(`Lang: ${lang}`)
window.location = `/${e.target.id}${path}${window.location.hash}`
});
})
})();
(function chapterNavigation() {
document.addEventListener('keydown', function (e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }

View File

@@ -83,6 +83,13 @@ body.sidebar-visible #menu-bar {
}
}
.right-buttons .icons {
display: flex;
flex-direction: row;
flex-wrap: wrap;
column-gap: 0.5rem;
}
.icon-button {
border: none;
background: var(--bg);
@@ -138,11 +145,13 @@ body.sidebar-visible #menu-bar {
}
/* Collapse Menu Popup */
#menubar-collapse-toggle {
position: relative;
}
#menubar-collapse-popup {
position: absolute;
right: 30px;
top: var(--menu-bar-height);
right: 0px;
top: 35px;
z-index: 105;
border-radius: 5px;
font-size: 14px;
@@ -172,6 +181,44 @@ body.sidebar-visible #menu-bar {
background-color: var(--theme-hover);
}
/* Languages Menu Popup */
#menubar-languages-toggle {
position: relative;
}
#menubar-languages-popup {
position: absolute;
right: 0px;
top: 35px;
z-index: 105;
border-radius: 5px;
font-size: 14px;
color: var(--fg);
background: var(--bg);
border: 1px solid var(--table-border-color);
margin: 0;
padding: 0px;
display: none;
flex-direction: column;
max-height: 300px;
width: 150px;
overflow: scroll;
}
#menubar-languages-popup .menu-bar-link {
border: 0;
margin: 0;
padding: 8px 20px;
line-height: 25px;
white-space: nowrap;
text-align: start;
cursor: pointer;
color: inherit;
background: inherit;
font-size: inherit;
}
#menubar-languages-popup .menu-bar-link:hover {
background-color: var(--theme-hover);
}
.left-buttons {
display: flex;

View File

@@ -144,34 +144,60 @@
<div class="right-buttons">
<div id="menubar-collapse">
<a class="menu-bar-link" href="https://training.hacktricks.xyz" target="_blank">
Hacktricks Training
</a>
<a class="menu-bar-link" href="https://twitter.com/hacktricks_live" target="_blank">
Twitter
</a>
<a class="menu-bar-link" href="https://www.linkedin.com/company/hacktricks" target="_blank">
Linkedin
</a>
<a class="menu-bar-link" href="https://github.com/sponsors/carlospolop" target="_blank">
Sponsor
</a>
</div>
<button id="menubar-collapse-toggle" class="icon-button" type="button" title="Toggle menu bar" aria-label="Toggle Menu bar" aria-expanded="false" aria-controls="collapse">
<i class="fa fa-ellipsis-h"></i>
</button>
<div id="menubar-collapse-popup" class="menubar-collapse-popup" aria-label="Menu" role="menu">
<a href="https://training.hacktricks.xyz" target="_blank" role="menuitem" class="menu-bar-link">Hacktricks Training</a>
<a href="https://twitter.com/hacktricks_live" target="_blank" role="menuitem" class="menu-bar-link">Twitter</a>
<a href="https://www.linkedin.com/company/hacktricks" target="_blank" role="menuitem" class="menu-bar-link">Linkedin</a>
<a href="https://github.com/sponsors/carlospolop" target="_blank" role="menuitem" class="menu-bar-link">Sponsor</a>
<a class="menu-bar-link" href="https://training.hacktricks.xyz" target="_blank">
Hacktricks Training
</a>
<a class="menu-bar-link" href="https://twitter.com/hacktricks_live" target="_blank">
Twitter
</a>
<a class="menu-bar-link" href="https://www.linkedin.com/company/hacktricks" target="_blank">
Linkedin
</a>
<a class="menu-bar-link" href="https://github.com/sponsors/carlospolop" target="_blank">
Sponsor
</a>
</div>
<div class="icons">
<div id="menubar-collapse-toggle" class="icon-button" type="button" title="Toggle menu bar" aria-label="Toggle Menu bar" aria-expanded="false" aria-controls="collapse">
<i class="fa fa-ellipsis-h"></i>
<div id="menubar-collapse-popup" class="menubar-collapse-popup" aria-label="Menu" role="menu">
<a href="https://training.hacktricks.xyz" target="_blank" role="menuitem" class="menu-bar-link">Hacktricks Training</a>
<a href="https://twitter.com/hacktricks_live" target="_blank" role="menuitem" class="menu-bar-link">Twitter</a>
<a href="https://www.linkedin.com/company/hacktricks" target="_blank" role="menuitem" class="menu-bar-link">Linkedin</a>
<a href="https://github.com/sponsors/carlospolop" target="_blank" role="menuitem" class="menu-bar-link">Sponsor</a>
</div>
</div>
{{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
{{/if}}
{{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
{{/if}}
<div id="menubar-languages-toggle" class="icon-button" type="button" title="Translations" aria-label="Toggle Tanslations" aria-expanded="false" aria-controls="translations">
<i class="fa fa-globe"></i>
<div id="menubar-languages-popup" class="menubar-languages-popup" aria-label="Language menu" role="language menu">
<button id="en" role="menuitem" class="menu-bar-link">English</button>
<button id="es" role="menuitem" class="menu-bar-link">Spanish</button>
<button id="fr" role="menuitem" class="menu-bar-link">French</button>
<button id="de" role="menuitem" class="menu-bar-link">German</button>
<button id="el" role="menuitem" class="menu-bar-link">Greek</button>
<button id="hi" role="menuitem" class="menu-bar-link">Hindi</button>
<button id="it" role="menuitem" class="menu-bar-link">Italian</button>
<button id="ja" role="menuitem" class="menu-bar-link">Japanese</button>
<button id="ko" role="menuitem" class="menu-bar-link">Korean</button>
<button id="pl" role="menuitem" class="menu-bar-link">Polish</button>
<button id="pt" role="menuitem" class="menu-bar-link">Portuguese</button>
<button id="sr" role="menuitem" class="menu-bar-link">Serbian</button>
<button id="sw" role="menuitem" class="menu-bar-link">Swahili</button>
<button id="tr" role="menuitem" class="menu-bar-link">Turkish</button>
<button id="uk" role="menuitem" class="menu-bar-link">Ukrainian</button>
<button id="af" role="menuitem" class="menu-bar-link">Afrikaans</button>
<button id="zh" role="menuitem" class="menu-bar-link">Chinese</button>
</div>
</div>
</div>
{{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">

View File

@@ -19,13 +19,11 @@
try {
const response = await fetch(url, { method: "GET" })
if (!response.ok) {
console.log(response)
throw new Error(`Response status: ${response.status}`)
}
const json = await response.json()
var sponsor = json.sponsor
console.log("boop", sponsor)
sponsorImg.src = sponsor.image_url
sponsorTitle.textContent = sponsor.name
sponsorDescription.innerHTML = sponsor.description