mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2025-12-19 10:42:44 -08:00
Translated ['src/pentesting-cloud/kubernetes-security/kubernetes-pivotin
This commit is contained in:
@@ -22,6 +22,7 @@ after = ["links"]
|
|||||||
|
|
||||||
[preprocessor.hacktricks]
|
[preprocessor.hacktricks]
|
||||||
command = "python3 ./hacktricks-preprocessor.py"
|
command = "python3 ./hacktricks-preprocessor.py"
|
||||||
|
env = "prod"
|
||||||
|
|
||||||
[output.html]
|
[output.html]
|
||||||
additional-css = ["theme/pagetoc.css", "theme/tabs.css"]
|
additional-css = ["theme/pagetoc.css", "theme/tabs.css"]
|
||||||
|
|||||||
@@ -30,14 +30,16 @@ def ref(matchobj):
|
|||||||
href = matchobj.groups(0)[0].strip()
|
href = matchobj.groups(0)[0].strip()
|
||||||
title = href
|
title = href
|
||||||
if href.startswith("http://") or href.startswith("https://"):
|
if href.startswith("http://") or href.startswith("https://"):
|
||||||
# pass
|
if context['config']['preprocessor']['hacktricks']['env'] == 'dev':
|
||||||
try:
|
pass
|
||||||
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())
|
else:
|
||||||
match = re.search('<title>(.*?)</title>', raw_html)
|
try:
|
||||||
title = match.group(1) if match else href
|
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())
|
||||||
except Exception as e:
|
match = re.search('<title>(.*?)</title>', raw_html)
|
||||||
logger.debug(f'Error opening URL {href}: {e}')
|
title = match.group(1) if match else href
|
||||||
pass #nDont stop on broken link
|
except Exception as e:
|
||||||
|
logger.debug(f'Error opening URL {href}: {e}')
|
||||||
|
pass #nDont stop on broken link
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if href.endswith("/"):
|
if href.endswith("/"):
|
||||||
|
|||||||
@@ -34,19 +34,19 @@ Copy codegcloud iam service-accounts keys create <key-file-name>.json \
|
|||||||
kubectl create secret generic <secret-name> \
|
kubectl create secret generic <secret-name> \
|
||||||
--from-file=key.json=<key-file-name>.json
|
--from-file=key.json=<key-file-name>.json
|
||||||
```
|
```
|
||||||
- Collega il Secret di Kubernetes all'account di servizio di Kubernetes utilizzando il seguente comando:
|
- Collega il Kubernetes Secret all'account di servizio Kubernetes utilizzando il seguente comando:
|
||||||
```bash
|
```bash
|
||||||
Copy codekubectl annotate serviceaccount <service-account-name> \
|
Copy codekubectl annotate serviceaccount <service-account-name> \
|
||||||
iam.gke.io/gcp-service-account=<gcp-service-account-email>
|
iam.gke.io/gcp-service-account=<gcp-service-account-email>
|
||||||
```
|
```
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Nel **secondo passo** sono state impostate le **credenziali del GSA come segreto del KSA**. Quindi, se puoi **leggere quel segreto** dall'**interno** del cluster **GKE**, puoi **escalare a quel servizio account GCP**.
|
> Nel **secondo passaggio** sono state impostate le **credenziali del GSA come segreto del KSA**. Quindi, se puoi **leggere quel segreto** dall'**interno** del cluster **GKE**, puoi **escalare a quel servizio account GCP**.
|
||||||
|
|
||||||
### Identità del Carico di Lavoro GKE
|
### Identità del Carico di Lavoro GKE
|
||||||
|
|
||||||
Con l'Identità del Carico di Lavoro, possiamo configurare un[ account di servizio Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) per agire come un[ account di servizio Google](https://cloud.google.com/iam/docs/understanding-service-accounts). I pod che girano con l'account di servizio Kubernetes si autenticheranno automaticamente come l'account di servizio Google quando accedono alle API di Google Cloud.
|
Con l'Identità del Carico di Lavoro, possiamo configurare un [Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) per agire come un [Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). I pod che girano con il Kubernetes service account si autenticheranno automaticamente come il Google service account quando accedono alle API di Google Cloud.
|
||||||
|
|
||||||
La **prima serie di passi** per abilitare questo comportamento è **abilitare l'Identità del Carico di Lavoro in GCP** ([**passi**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) e creare il SA GCP che vuoi che k8s impersoni.
|
La **prima serie di passaggi** per abilitare questo comportamento è **abilitare l'Identità del Carico di Lavoro in GCP** ([**passaggi**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) e creare il GCP SA che vuoi che k8s impersoni.
|
||||||
|
|
||||||
- **Abilita l'Identità del Carico di Lavoro** su un nuovo cluster
|
- **Abilita l'Identità del Carico di Lavoro** su un nuovo cluster
|
||||||
```bash
|
```bash
|
||||||
@@ -126,7 +126,7 @@ gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-
|
|||||||
> Come attaccante all'interno di K8s dovresti **cercare SAs** con l'**annotazione `iam.gke.io/gcp-service-account`** poiché indica che l'SA può accedere a qualcosa in GCP. Un'altra opzione sarebbe cercare di abusare di ogni KSA nel cluster e controllare se ha accesso.\
|
> Come attaccante all'interno di K8s dovresti **cercare SAs** con l'**annotazione `iam.gke.io/gcp-service-account`** poiché indica che l'SA può accedere a qualcosa in GCP. Un'altra opzione sarebbe cercare di abusare di ogni KSA nel cluster e controllare se ha accesso.\
|
||||||
> Da GCP è sempre interessante enumerare i binding e sapere **quale accesso stai dando agli SAs all'interno di Kubernetes**.
|
> Da GCP è sempre interessante enumerare i binding e sapere **quale accesso stai dando agli SAs all'interno di Kubernetes**.
|
||||||
|
|
||||||
Questo è uno script per **iterare facilmente su tutte le definizioni dei pod** **cercando** quella **annotazione**:
|
Questo è uno script per **iterare facilmente su tutte le definizioni dei pod** **cercando** quell'**annotazione**:
|
||||||
```bash
|
```bash
|
||||||
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
|
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
|
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
|
||||||
@@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: |
|
|||||||
["role-arn"]
|
["role-arn"]
|
||||||
name: default
|
name: default
|
||||||
```
|
```
|
||||||
Una volta che il namespace è configurato con i ruoli IAM che i Pods possono avere, puoi **indicare il ruolo che desideri in ciascuna definizione del pod con qualcosa come**:
|
Una volta che lo spazio dei nomi è configurato con i ruoli IAM che i Pod possono avere, puoi **indicare il ruolo che desideri in ciascuna definizione del pod con qualcosa come**:
|
||||||
```yaml:Kiam & Kube2iam
|
```yaml:Kiam & Kube2iam
|
||||||
kind: Pod
|
kind: Pod
|
||||||
metadata:
|
metadata:
|
||||||
@@ -194,7 +194,7 @@ args: ["-c", "sleep 100000"]' | kubectl apply -f -
|
|||||||
```
|
```
|
||||||
### IAM Role per gli Account di Servizio K8s tramite OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
|
### IAM Role per gli Account di Servizio K8s tramite OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
|
||||||
|
|
||||||
Questo è il **metodo raccomandato da AWS**.
|
Questo è il **modo raccomandato da AWS**.
|
||||||
|
|
||||||
1. Prima di tutto, è necessario [creare un provider OIDC per il cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
|
1. Prima di tutto, è necessario [creare un provider OIDC per il cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
|
||||||
2. Poi si crea un ruolo IAM con i permessi di cui avrà bisogno l'SA.
|
2. Poi si crea un ruolo IAM con i permessi di cui avrà bisogno l'SA.
|
||||||
@@ -234,7 +234,7 @@ aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/
|
|||||||
../aws-security/aws-basic-information/aws-federation-abuse.md
|
../aws-security/aws-basic-information/aws-federation-abuse.md
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
### Trova Pods e SAs con Ruoli IAM nel Cluster
|
### Trova Pods e SAs con IAM Roles nel Cluster
|
||||||
|
|
||||||
Questo è uno script per **iterare facilmente su tutti i pod e le definizioni di sas** **cercando** quella **annotazione**:
|
Questo è uno script per **iterare facilmente su tutti i pod e le definizioni di sas** **cercando** quella **annotazione**:
|
||||||
```bash
|
```bash
|
||||||
@@ -263,7 +263,7 @@ kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"s
|
|||||||
```
|
```
|
||||||
### Rubare il Token del Ruolo IAM
|
### Rubare il Token del Ruolo IAM
|
||||||
|
|
||||||
In precedenza abbiamo discusso di come **allegare i Ruoli IAM ai Pod** o addirittura di come **fuggire al Nodo per rubare il Ruolo IAM** che l'istanza ha allegato ad esso.
|
In precedenza abbiamo discusso di come **allegare i Ruoli IAM ai Pod** o addirittura di come **fuggire al Nodo per rubare il Ruolo IAM** che l'istanza ha ad esso allegato.
|
||||||
|
|
||||||
Puoi utilizzare il seguente script per **rubare** le tue nuove e faticosamente guadagnate **credenziali del ruolo IAM**:
|
Puoi utilizzare il seguente script per **rubare** le tue nuove e faticosamente guadagnate **credenziali del ruolo IAM**:
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -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() {
|
(function chapterNavigation() {
|
||||||
document.addEventListener('keydown', function (e) {
|
document.addEventListener('keydown', function (e) {
|
||||||
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||||
|
|||||||
@@ -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 {
|
.icon-button {
|
||||||
border: none;
|
border: none;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
@@ -138,11 +145,13 @@ body.sidebar-visible #menu-bar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Collapse Menu Popup */
|
/* Collapse Menu Popup */
|
||||||
|
#menubar-collapse-toggle {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
#menubar-collapse-popup {
|
#menubar-collapse-popup {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 30px;
|
right: 0px;
|
||||||
top: var(--menu-bar-height);
|
top: 35px;
|
||||||
z-index: 105;
|
z-index: 105;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -172,6 +181,44 @@ body.sidebar-visible #menu-bar {
|
|||||||
background-color: var(--theme-hover);
|
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 {
|
.left-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -144,34 +144,60 @@
|
|||||||
|
|
||||||
<div class="right-buttons">
|
<div class="right-buttons">
|
||||||
<div id="menubar-collapse">
|
<div id="menubar-collapse">
|
||||||
<a class="menu-bar-link" href="https://training.hacktricks.xyz" target="_blank">
|
<a class="menu-bar-link" href="https://training.hacktricks.xyz" target="_blank">
|
||||||
Hacktricks Training
|
Hacktricks Training
|
||||||
</a>
|
</a>
|
||||||
<a class="menu-bar-link" href="https://twitter.com/hacktricks_live" target="_blank">
|
<a class="menu-bar-link" href="https://twitter.com/hacktricks_live" target="_blank">
|
||||||
Twitter
|
Twitter
|
||||||
</a>
|
</a>
|
||||||
<a class="menu-bar-link" href="https://www.linkedin.com/company/hacktricks" target="_blank">
|
<a class="menu-bar-link" href="https://www.linkedin.com/company/hacktricks" target="_blank">
|
||||||
Linkedin
|
Linkedin
|
||||||
</a>
|
</a>
|
||||||
<a class="menu-bar-link" href="https://github.com/sponsors/carlospolop" target="_blank">
|
<a class="menu-bar-link" href="https://github.com/sponsors/carlospolop" target="_blank">
|
||||||
Sponsor
|
Sponsor
|
||||||
</a>
|
</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>
|
|
||||||
</div>
|
</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}}
|
{{#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">
|
<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>
|
<i class="fa fa-search"></i>
|
||||||
</button>
|
</button>
|
||||||
{{/if}}
|
{{/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}}
|
{{#if print_enable}}
|
||||||
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
||||||
|
|||||||
@@ -19,13 +19,11 @@
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(url, { method: "GET" })
|
const response = await fetch(url, { method: "GET" })
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.log(response)
|
|
||||||
throw new Error(`Response status: ${response.status}`)
|
throw new Error(`Response status: ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
var sponsor = json.sponsor
|
var sponsor = json.sponsor
|
||||||
console.log("boop", sponsor)
|
|
||||||
sponsorImg.src = sponsor.image_url
|
sponsorImg.src = sponsor.image_url
|
||||||
sponsorTitle.textContent = sponsor.name
|
sponsorTitle.textContent = sponsor.name
|
||||||
sponsorDescription.innerHTML = sponsor.description
|
sponsorDescription.innerHTML = sponsor.description
|
||||||
|
|||||||
Reference in New Issue
Block a user