Návod: VPN WireGuard na routerech MikroTik a telefonech s Androidem

26. 1. 2022
Doba čtení: 6 minut

Sdílet

Autor: MikroTik, Wireguard
Návod pro ty, kteří chtějí rozchodit VPN pomocí nástroje WireGuard a použít k tomu MikroTik. Ukážeme si, jak se k síti připojit s telefonem s operačním systémem Android.

WireGuard je jednoduchá VPN, která vznikla původně pro linux a velmi rychle se rozšířila do zbytku počítačového světa. Vyniká především svou bezpečností a nenáročností na konfiguraci a obsluhu. Další detaily najdete v našem seriálu o WireGuardu, případně si můžete vše vyzkoušet v našem samostatném školení WireGuard.

Co se dozvíte v článku
  1. MikroTik
  2. Totéž na příkazové řádce
  3. Telefon s Androidem
  4. Často kladené otázky

MikroTik

Síťové prvky lotyšské společnosti MikroTik jsou oblíbené zejména díky rozumné ceně, za kterou nabízejí ohromné množství funkcí a dlouhou podporu. Položka WireGuard je součástí operačního systému RouterOS od čerstvé verze 7, která vyšla na konci loňského roku. Po přihlášení na zařízení se ujistěte, že máte tuto verzi nainstalovanou v System → Packages.


Autor: Petr Krčmář, Root.cz

Že je vše připraveno poznáte také podle toho, že v menu v rozhraní WebFig uvidíte samostatnou položku WireGuard s modrou ikonou.

1. Vytvoření rozhraní

Nejprve musíme vytvořit nové rozhraní typu WireGuard. Zvolíme tedy WireGuard → karta WireGuard → Add New. Otevře se nám formulář pro vytvoření nového rozhraní, ve kterém můžeme rozhraní nějak pojmenovat a poté klepneme na Apply. Tím rozhraní vznikne a automaticky se mu přidělí UDP port a vygenerují se soukromý i veřejný klíč.


Autor: Petr Krčmář, Root.cz

Soukromý klíč by neměl nikdy opustit prostředí našeho routeru, budeme potřebovat jen klíč veřejný, který přeneseme do všech zařízení (peer), která budeme chtít ke svému MikroTiku připojit.

2. Přidání protistrany

Pro přidání protistrany budeme potřebovat znát její veřejný klíč. Každý klient je schopen jej vygenerovat. V Linuxu to provedeme příkazem wg genkey, celý postup najdete v samostatném článku. Postup pro Android naleznete níže. Pokud byste spojovali dva MikroTiky, na druhém z nich zopakujete předchozí krok – založení rozhraní. Tím získáte jeho veřejný klíč.

Otevřeme WireGuard → kartu Peers → Add New. Otevře se nám formulář pro přidání nového partnera. Musíme přidat minimálně jeho veřejný klíč a IP adresu povolenou uvnitř naší VPN. Adresní rozsah naší nové virtuální sítě nesmí kolidovat s žádnou skutečnou sítí, do které jsme připojeni. Dejme tomu, že uvnitř VPN budeme používat rozsah 10.200.0.0/24. MikroTik dostane 10.200.0.1 a náš první klient bude mít 10.200.0.2. Vložíme tedy jeho adresu do příslušného formuláře.

Volitelně můžeme uvést skutečnou adresu (Endpoint) a port protistrany (Endpoint Port), což dává smysl jen v případě, že je pro nás protistrana síťově dostupná. Pokud by naši protistranu představoval linuxový server s veřejnou adresou, napíšeme ji sem. Alespoň jeden z partnerů musí mít tuto položku vyplněnou, aby se komunikace navázala. Předpokládejme, že náš MikroTik má veřejnou adresu a klient ji mít obvykle nebude, proto ji sem teď nevyplníme.

Dále máme možnost sem vyplnit čas, po kterém budou protistraně posílány prázdné pakety pro udržení spojení přes NAT. Můžeme také přidat komentář (Comment), abychom se v jednotlivých zařízeních vyznali.


Autor: Petr Krčmář, Root.cz

Nyní stačí stisknout OK a přidali jsme prvního partnera. Pokud budeme mít ve své VPN další zařízení, musíme je takto všechna přidat. Na každém vytvoříme pár klíčů, přidělíme mu jednu vnitřní IP adresu z rozsahu a přidáme ho jako položku na MikroTiku.

3. Přidání adresy na rozhraní

Máme zvolený adresní rozsah a jednu z adres jsme přidělili klientovi. Zatím ale nemá svou adresu přidělenu samotný MikroTik. Proto navštívíme položku IP → Addresses → Add New. Tady vyplníme jen adresu přidělenou v rámci VPN, nezapomeňte přidělit správný prefix pro celou síť a přidělit ji na rozhraní WireGuardu podle jména.


Autor: Petr Krčmář, Root.cz

Tím jsme přidělili IP adresu také na rozhraní WireGuardu v MikroTiku.

Tím máme hotovo. Pokud jsme postupovali správně, máme vytvořené rozhraní WireGuardu, přidělenou adresu a zařízené směrování provozu z nové sítě do správného rozhraní. Přidali jsme také prvního partnera, kterým může být například náš telefon s Androidem. Nyní zbývá ještě nastavit druhou stranu.

Totéž na příkazové řádce

Pokud se na MikroTiku kamarádíte více s příkazovou řádkou než s webovým rozhraním WebFig, můžete totéž udělat pomocí tří příkazů. U druhého z nich nezapomeňte doplnit veřejný klíč.

/interface/wireguard/add name=wireguard1 mtu=1420 listen-port=13231
/interface/wireguard/peers/add interface=wireguard1 allowed-address=10.200.0.2/32 persistent-keepalive=30s comment="telefon s androidem" public-key="FIXME"
/ip/address/add interface=wireguard1 address=10.200.0.1/24

Telefon s Androidem

Na Androidu je potřeba nainstalovat aplikaci WireGuard z Google Play nebo z jiného zdroje (třeba F-Droid). Pro její použití budeme potřebovat znát veřejný klíč ze svého MikroTiku a také jeho IP adresu a UDP port, na kterém poslouchá jeho WireGuard. To vše vyčteme z webového rozhraní, které jsme před chvílí použili.

Spustíme aplikaci WireGuard a v ní vpravo dole stiskneme tlačítko plus. V menu můžeme zvolit, zda chceme importovat připravenou konfiguraci ze souboru, načísti ji z QR kódu nebo ji vytvořit ručně přímo v telefonu. Konfiguraci bychom si museli vytvořit na jiném zařízení, což jsme neudělali a není to ani potřeba. Vybereme tedy třetí položku a připravíme vše přímo na displeji.


Autor: Petr Krčmář, Root.cz

Nejprve vyplníme název své nové sítě, poté klepneme na dvojitou šipku a necháme si tak jednoduše vygenerovat pár klíčů. Ten soukromý je nám skrytý a objeví se po kliknutí na puntíky a prokázání totožnosti (obvykle otiskem). My ho ale nepotřebujeme vidět, stačí nám veřejný klíč. Ten se po klepnutí zkopíruje do schránky a my si ho můžeme vložit do libovolné jiné aplikace a poslat třeba jako poznámku.

Do formuláře musíme ještě vyplnit svou adresu uvnitř VPN a také nějaký použitelný DNS resolver. Tím jsme nastavili svoje místní rozhraní.


Autor: Petr Krčmář, Root.cz

Pod tímto formulářem je tlačítko Přidat peera, které nám umožní vyplnit další formulář, který se týká našeho partnera. U něj musíme vyplnit veřejný klíč, skutečnou IP adresu (Endpoint) a povolené IP adresy, které nám od něj mohou přicházet. Volitelně můžeme ještě opět zvolit čas posílání prázdných paketů pro udržení spojení přes NAT (Persistent keepalive).


Autor: Petr Krčmář, Root.cz

Teď už stačí jen uložit pomocí ikony disketky v pravém horním rohu. Tím se nám vytvoří nová VPN v Androidu. Takto si můžeme vytvořit několik různých sítí a všechny je sem přidat. Pokud chceme některou z nich aktivovat, přepneme posuvný přepínač u jejího názvu. Nahoře v liště se nám objeví klíček, který značí, že telefon v danou chvíli využívá služeb VPN.

docker + kubernetes školení s dotací tip

Pokud bychom chtěli přes VPN posílat provoz z větší sítě nebo z celého internetu, musíme povolit přijetí celého rozsahu, případně rovnou 0.0.0.0/0, což značí libovolnou adresu. Pro IPv6 uvedeme adekvátní ::0/0. Pokud bychom chtěli uvést více různých rozsahů, oddělíme je čárkou.

V tuto chvíli jsme schopni se jedním tapnutím připojit k MikroTiku, vytvořit tunel a obě zařízení spolu mohou komunikovat. Pokud to v seznamu IP adres na Androidu povolíme, může nám MikroTik posílat provoz z celé sítě a my do ní máme přístup a můžeme využívat jejich služeb.

Často kladené otázky

Od které verze je v RouterOS podpora VPN WireGuard?
Podpora je dostupná od RouterOS verze 7.
Kde vezmu veřejný klíč partnera (peera)?
Každý klient je schopen vygenerovat pár klíčů, můžeme z něj tedy veřejný klíč zkopírovat a přenést na druhou stranu.
Jaké síťové porty používá WireGuard?
WireGuard vždy používá pro spojení jeden UDP port, který si zvolíte. Nejčastěji je to 51820, ale může to být libovolný jiný volný port.
Musím mít pro použití WireGuardu veřejnou IP adresu?
Obě komunikující strany musejí být síťově dostupné, musejí se tedy na síti vidět. Pokud budete spojovat dvě zařízení ve vlastní síti, stačí vám privátní adresy. Pokud se budete chtít připojovat přes internet, alespoň jedna strana musí mít veřejnou adresu.
Projde WireGuard přes překlad adres (NAT)?
WireGuard nemá s průchodem přes NAT nejmenší problém. Jediný háček je v tom, že pokud obě strany zrovna nekomunikují, nepřenášejí se po síti žádná data. To obvykle vede u uzavření cesty přes NAT. Proto je možné v konfiguraci nastavit udržování cesty pomocí periodického odesílání paketů zvaných keepalive.

Autor článku

Petr Krčmář pracuje jako šéfredaktor serveru Root.cz. Studoval počítače a média, takže je rozpolcen mezi dva obory. Snaží se dělat obojí, jak nejlépe umí.

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »