diff --git a/.github/workflows/dev-docker-build.yml b/.github/workflows/dev-docker-build.yml new file mode 100644 index 0000000..e56160a --- /dev/null +++ b/.github/workflows/dev-docker-build.yml @@ -0,0 +1,40 @@ +name: DEV docker image auto build + +on: + push: + branches: [ "dev" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + context: ./ + file: ./Dockerfile + platform: linux/arm/v7,linux/arm/v6,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: moebiuss/noip-renew:dev + + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..4e0ef25 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,57 @@ +name: Docker Image CI + +on: + push: + tags: + - "v*.*.*" + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Prepare + id: prep + run: | + DOCKER_IMAGE=moebiuss/noip-renew + VERSION=edge + if [[ $GITHUB_REF == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/v} + fi + if [ "${{ github.event_name }}" = "schedule" ]; then + VERSION=nightly + fi + TAGS="${DOCKER_IMAGE}:${VERSION}" + if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + TAGS="$TAGS,${DOCKER_IMAGE}:latest" + fi + echo ::set-output name=tags::${TAGS} + + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + context: ./ + platforms: linux/amd64,linux/arm64,linux/arm + file: ./Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.prep.outputs.tags }} + + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/.gitignore b/.gitignore index e7d5212..24b4808 100644 --- a/.gitignore +++ b/.gitignore @@ -99,6 +99,12 @@ ENV/ # mypy .mypy_cache/ -debug1.png -debug2.png + +.bash_history +.pki +.wget-hsts +*success.png +exception.png +debug*.png results.png +vps_mon.py diff --git a/Dockerfile b/Dockerfile index 25b1788..444b5d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,18 @@ FROM debian LABEL maintainer="loblab" -#ARG TZ=Asia/Shanghai -#ARG APT_MIRROR=mirrors.163.com ARG DEBIAN_FRONTED=noninteractive ARG PYTHON=python3 -#RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -#RUN sed -i "s/deb.debian.org/$APT_MIRROR/" /etc/apt/sources.list +ENV CONTAINER=1 + RUN apt-get update && apt-get -y upgrade RUN apt-get -y install chromium-chromedriver || \ apt-get -y install chromium-driver || \ apt-get -y install chromedriver RUN apt-get -y install ${PYTHON}-pip -RUN $PYTHON -m pip install selenium +RUN apt-get -y install ${PYTHON}-selenium +RUN apt-get -y install ${PYTHON}-pyotp RUN apt-get -y install curl wget RUN mkdir -p /home/loblab && \ @@ -22,4 +21,4 @@ RUN mkdir -p /home/loblab && \ USER loblab WORKDIR /home/loblab COPY /noip-renew.py /home/loblab/ -ENTRYPOINT ["python3", "/home/loblab/noip-renew.py"] +ENTRYPOINT ["python3", "/home/loblab/noip-renew.py"] \ No newline at end of file diff --git a/README.md b/README.md index 9246368..3a17db4 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,39 @@ +[![Docker Image CI](https://github.com/neothematrix/noip-renew/actions/workflows/docker-image.yml/badge.svg)](https://github.com/neothematrix/noip-renew/actions/workflows/docker-image.yml) + # Script to auto renew/confirm noip.com free hosts [noip.com](https://www.noip.com/) free hosts expire every month. This script auto clicks web pages to renew the hosts, using Python/Selenium with Chrome headless mode. -- Platform: Debian/Ubuntu/Raspbian/Arch Linux, no GUI needed (tested on Debian 9.x/10.x/Arch Linux); python 3.6+ -- Ver: 1.2 +NOTE: this is an up-to-date fork of loblab/noip-renew repository as it seems it's not anymore actively developed, I'll try to keep this fork up to date and working as much as possible. Feel free to contribute! + +- Platform: Debian/Ubuntu/Raspbian/Arch Linux, no GUI needed (tested on Debian 9.x/10.x/11.x/Arch Linux); python 3.5+ +- Chrome webdriver - tested up to version 120.0.6099.102-rpt1 +- Selenium - v.3.x (python 3.5+); v. 4.0.0 (python 3.7+); v.4.10.0 and above (python 3.8+); Tested up to v. 4.17.2 - Ref: [Technical explanation for the code (Chinese)](http://www.jianshu.com/p/3c8196175147) -- Updated: 1/2/2021 - Created: 11/04/2017 -- Author: loblab -- Contributor: [IDemixI](https://www.github.com/IDemixI) +- Original Author: loblab +- Fork Mantainer: neothematrix +- Contributors: [Angel0ffDeath](https://github.com/Angel0ffDeath), [benyjr](https://github.com/benyjr) ![noip.com hosts](https://raw.githubusercontent.com/loblab/noip-renew/master/screenshot.png) +## Prerequisites + +ENABLE 2FA authentication on your account and save the 2FA Secret key that is shared only once when you activate it + +ChromeDriver is required for the script to interface with noip.com from within the script. +ChromeDriver must be maintained to match the installed version of Chrome. +There is no automated repository that provides ChromeDriver package beyond Ubuntu 18 LTS. +Installing Chromium via Snap and then linking chromedriver to /usr/bin/chromedriver should keep chromediver sufficiently up to date without manual intervention + ## Usage -1. Clone this repository to the device you will be running it from. (`git clone https://github.com/loblab/noip-renew.git`) +1. Clone this repository to the device you will be running it from. (`git clone https://github.com/neothematrix/noip-renew.git`) 2. Run setup.sh and set your noip.com account information, -3. Run noip-renew-USERNAME command. +3. If you want to use randomized cronjob answer y to the question and give time interval hours, i.e. 7-18 (for instance). This will give a window from 7:00 to 18:59; Take into account it is possible script to try to run on days on which Daylight Saving Time (DST) occurs (if you have such thing in your country), so avoid hours in which this event occurs, otherwise the scrippt may not run. For instance in Europe DST events are last Sundays of March and October at 3AM (March) or 4AM (October). Script probably will not run on the DST day in March if scheduled between 3 and 4 AM. It is too complicated to incorporate such logic, but if someone would like then try. +4. Run noip-renew-USERNAME command. Check confirmed records from multiple log files: @@ -27,36 +42,24 @@ grep -h Confirmed *.log | grep -v ": 0" | sort ``` ## Usage with Docker -For docker users, run the following: +For docker users you need to define the following ENV variables: + +``` +'NOIP_USERNAME = ' +'NOIP_PASSWORD = ' +'NOIP_2FA_SECRET_KEY = ' +NOIP_DEBUG = +``` + +so you can run the following: ```sh -my_username='add username here' -my_password='add password here' -my_host_num='add number of hosts here' -debug_lvl=2 -docker build -t loblab/selenium:debian . -echo -e "$(crontab -l)"$'\n'"12 3 * * 1,3,5 docker run --network host loblab/selenium:debian ${my_username} ${my_password} ${my_host_num} ${debug_lvl}" | crontab - +echo -e "$(crontab -l)"$'\n'"12 3 * * 1,3,5 docker run --rm --network host -e 'NOIP_USERNAME=' -e 'NOIP_PASSWORD=' -e 'NOIP_2FA_SECRET_KEY=' -e NOIP_DEBUG=2 moebiuss/noip-renew" | crontab - ``` +NOTE: with newer versions of ChromeDriver (>v99) you might need to increase the shm size of the container otherwise ChromeDriver will crash and throw an exception. To do it, you can just add the "--shm-size="512m" flag to the docker run command. ## Remarks -The script is not designed to renew/update the dynamic DNS records, though the latest version does have this ability if requested. +The script is not designed to renew/update the dynamic DNS records, but only to renew the hostnames expiring every 30 days due to the free tier. Check [noip.com documentation](https://www.noip.com/integrate) for that purpose. Most wireless routers support noip.com. For more information, check [here](https://www.noip.com/support/knowledgebase/what-devices-support-no-ips-dynamic-dns-update-service/). You can also check [DNS-O-Matic](https://dnsomatic.com/) to update multiple noip.com DNS records. - -If you need notification functionality, please try [IDemixI's branch](https://github.com/IDemixI/noip-renew/tree/notifications). - -## History -- 1.2 (01/02/2021): Merged all pull requests in latest months: make it work for updated noip.com site. -- 1.1 (06/05/2020): Fixed error when attempting to update an expired host. -- 1.0 (05/18/2020): Minor fixes to an xpath & a try catch pass to avoid an exception. Also fixed versioning. -- 1.0 (04/16/2020): Catches "Would you like to upgrade?" page & stops script accordingly. Manual intervention still required. -- 0.9 (04/13/2020): Complete refactor of code, more stability & automatic crontab scheduling. -- 0.8 (03/23/2020): Added menu to repair/install/remove script along with ability to update noip.com details. -- 0.7 (03/21/2020): Code tidyup and improved efficiency (Removed number of hosts and automatically get this) -- 0.6 (03/15/2020): Improved support for Raspberry Pi (Raspbian Buster) & Changes to setup script. -- 0.5 (01/05/2020): Support raspberry pi, try different "chromedriver" packages in setup script. -- 0.4 (01/14/2019): Add num_hosts argument, change for button renaming; support user agent. -- 0.3 (05/19/2018): Support Docker, ignore timeout, support proxy, tested on python3. -- 0.2 (11/12/2017): Deploy the script as normal user only. root user with 'no-sandbox' option is not safe for Chrome. -- 0.1 (11/05/2017): Support Debian with Chrome headless. diff --git a/noip-renew-skd.sh b/noip-renew-skd.sh index b7f5dab..48bd83b 100755 --- a/noip-renew-skd.sh +++ b/noip-renew-skd.sh @@ -1,11 +1,13 @@ #!/bin/bash +Min=30 +Hour=0 USER= SUDO=sudo LOGDIR=/var/log/noip-renew/$USER INSTDIR=/usr/local/bin -INSTEXE=$INSTDIR/noip-renew-$USER -CRONJOB="30 0 * * * $USER $INSTEXE $LOGDIR" -NEWCJOB="30 0 $1 $2 * $USER $INSTEXE $LOGDIR" +INSTEXE=$INSTDIR/noip-renew-$USER.sh +CRONJOB="$Min $Hour * * * $INSTEXE $LOGDIR" +NEWCJOB="$Min $Hour $1 $2 * $INSTEXE $LOGDIR" $SUDO crontab -u $USER -l | grep -v '/noip-renew*' | $SUDO crontab -u $USER - if [[ $3 = "True" ]]; then ($SUDO crontab -u $USER -l; echo "$NEWCJOB") | $SUDO crontab -u $USER - diff --git a/noip-renew.py b/noip-renew.py index ad3d7a9..978429a 100755 --- a/noip-renew.py +++ b/noip-renew.py @@ -15,8 +15,14 @@ from selenium import webdriver from selenium.common.exceptions import TimeoutException +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.common.by import By +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC from datetime import date from datetime import timedelta +from pyotp import * import time import sys import os @@ -24,6 +30,9 @@ import base64 import subprocess +VERSION = "2.0.3" +DOCKER = False + class Logger: def __init__(self, level): self.level = 0 if level is None else level @@ -39,12 +48,14 @@ class Robot: USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0" LOGIN_URL = "https://www.noip.com/login" - HOST_URL = "https://my.noip.com/#!/dynamic-dns" + HOST_URL = "https://my.noip.com/dynamic-dns" - def __init__(self, username, password, debug): + def __init__(self, username, password, totp_secret, debug, docker): self.debug = debug + self.docker = docker self.username = username self.password = password + self.totp_secret = totp_secret self.browser = self.init_browser() self.logger = Logger(debug) @@ -57,6 +68,7 @@ def init_browser(): options.add_argument("no-sandbox") # need when run in docker options.add_argument("window-size=1200x800") options.add_argument(f"user-agent={Robot.USER_AGENT}") + options.add_argument("disable-gpu") if 'https_proxy' in os.environ: options.add_argument("proxy-server=" + os.environ['https_proxy']) browser = webdriver.Chrome(options=options) @@ -66,38 +78,77 @@ def init_browser(): def login(self): self.logger.log(f"Opening {Robot.LOGIN_URL}...") self.browser.get(Robot.LOGIN_URL) + + try: + elem = WebDriverWait(self.browser, 10).until( EC.presence_of_element_located((By.ID, "content"))) + except: + raise Exception("Login page could not be loaded") + if self.debug > 1: self.browser.save_screenshot("debug1.png") self.logger.log("Logging in...") - ele_usr = self.browser.find_element_by_name("username") - ele_pwd = self.browser.find_element_by_name("password") + + ele_usr = elem.find_element(By.NAME, "username") + ele_pwd = elem.find_element(By.NAME, "password") + ele_usr.send_keys(self.username) - ele_pwd.send_keys(base64.b64decode(self.password).decode('utf-8')) - self.browser.find_element_by_name("Login").click() + + # If running on docker, password is not base64 encoded + if self.docker: + ele_pwd.send_keys(self.password) + else: + ele_pwd.send_keys(base64.b64decode(self.password).decode('utf-8')) + ele_pwd.send_keys(Keys.ENTER) + + try: + elem = WebDriverWait(self.browser, 10).until( EC.presence_of_element_located((By.ID, "verificationCode"))) + except: + raise Exception("2FA verify page could not load") + + if self.debug > 1: + self.browser.save_screenshot("debug-otp.png") + + self.logger.log("Sending OTP...") + + ele_challenge = elem.find_element(By.NAME, "challenge_code") + self.browser.execute_script("arguments[0].focus();", ele_challenge) + ActionChains(self.browser).send_keys(TOTP(self.totp_secret).now()).perform() + ActionChains(self.browser).send_keys(Keys.ENTER).perform() + + # After Loggin browser loads my.noip.com page - give him some time to load + # 'noip-cart' element is near the end of html, so html have been loaded + try: + elem = WebDriverWait(self.browser, 10).until( EC.presence_of_element_located((By.ID, "noip-cart"))) + except: + raise Exception("my.noip.com page could not load") + if self.debug > 1: - time.sleep(1) self.browser.save_screenshot("debug2.png") def update_hosts(self): count = 0 self.open_hosts_page() - time.sleep(1) + self.browser.implicitly_wait(5) iteration = 1 next_renewal = [] hosts = self.get_hosts() for host in hosts: host_link = self.get_host_link(host, iteration) # This is for if we wanted to modify our Host IP. - host_button = self.get_host_button(host, iteration) # This is the button to confirm our free host host_name = host_link.text expiration_days = self.get_host_expiration_days(host, iteration) - next_renewal.append(expiration_days) - self.logger.log(f"{host_name} expires in {str(expiration_days)} days") - if expiration_days < 7: + if expiration_days <= 7: + host_button = self.get_host_button(host, iteration) # This is the button to confirm our free host self.update_host(host_button, host_name) + expiration_days = self.get_host_expiration_days(host, iteration) + next_renewal.append(expiration_days) + self.logger.log(f"{host_name} expires in {str(expiration_days)} days") count += 1 + else: + next_renewal.append(expiration_days) + self.logger.log(f"{host_name} expires in {str(expiration_days)} days") iteration += 1 self.browser.save_screenshot("results.png") self.logger.log(f"Confirmed hosts: {count}", 2) @@ -105,7 +156,11 @@ def update_hosts(self): today = date.today() + timedelta(days=nr) day = str(today.day) month = str(today.month) - subprocess.call(['/usr/local/bin/noip-renew-skd.sh', day, month, "True"]) + if not self.docker: + try: + subprocess.call(['/usr/local/bin/noip-renew-skd.sh', day, month, "True"]) + except (FileNotFoundError,PermissionError): + self.logger.log(f"noip-renew-skd.sh missing or not executable, skipping crontab configuration") return True def open_hosts_page(self): @@ -119,10 +174,10 @@ def open_hosts_page(self): def update_host(self, host_button, host_name): self.logger.log(f"Updating {host_name}") host_button.click() - time.sleep(3) + self.browser.implicitly_wait(3) intervention = False try: - if self.browser.find_elements_by_xpath("//h2[@class='big']")[0].text == "Upgrade Now": + if self.browser.find_elements(By.XPATH, "//h2[@class='big']")[0].text == "Upgrade Now": intervention = True except: pass @@ -135,11 +190,13 @@ def update_host(self, host_button, host_name): @staticmethod def get_host_expiration_days(host, iteration): try: - host_remaining_days = host.find_element_by_xpath(".//a[@class='no-link-style']").text + host_remaining_days = host.find_element(By.XPATH, ".//a[contains(@class,'no-link-style')]") except: - host_remaining_days = "Expires in 0 days" - pass - regex_match = re.search("\\d+", host_remaining_days) + return 0 + if host_remaining_days.get_attribute("data-original-title") is not None: + regex_match = re.search("\\d+", host_remaining_days.get_attribute("data-original-title")) + else: + regex_match = re.search("\\d+", host_remaining_days.text) if regex_match is None: raise Exception("Expiration days label does not match the expected pattern in iteration: {iteration}") expiration_days = int(regex_match.group(0)) @@ -147,20 +204,21 @@ def get_host_expiration_days(host, iteration): @staticmethod def get_host_link(host, iteration): - return host.find_element_by_xpath(".//a[@class='link-info cursor-pointer']") + return host.find_element(By.XPATH, ".//a[@class='link-info cursor-pointer notranslate']") @staticmethod def get_host_button(host, iteration): - return host.find_element_by_xpath(".//following-sibling::td[4]/button[contains(@class, 'btn')]") + return host.find_element(By.XPATH, "//td[6]/button[contains(@class, 'btn-success')]") def get_hosts(self): - host_tds = self.browser.find_elements_by_xpath("//td[@data-title=\"Host\"]") + host_tds = self.browser.find_elements(By.XPATH, "//td[@data-title=\"Host\"]") if len(host_tds) == 0: raise Exception("No hosts or host table rows not found") return host_tds def run(self): rc = 0 + self.logger.log(f"No-IP renew script version {VERSION}") self.logger.log(f"Debug level: {self.debug}") try: self.login() @@ -169,7 +227,11 @@ def run(self): except Exception as e: self.logger.log(str(e)) self.browser.save_screenshot("exception.png") - subprocess.call(['/usr/local/bin/noip-renew-skd.sh', "*", "*", "False"]) + if not self.docker: + try: + subprocess.call(['/usr/local/bin/noip-renew-skd.sh', "*", "*", "False"]) + except (FileNotFoundError,PermissionError): + self.logger.log(f"noip-renew-skd.sh missing or not executable, skipping crontab configuration") rc = 2 finally: self.browser.quit() @@ -177,23 +239,34 @@ def run(self): def main(argv=None): - noip_username, noip_password, debug, = get_args_values(argv) - return (Robot(noip_username, noip_password, debug)).run() + # check if we're running on docker + DOCKER = os.environ.get("CONTAINER", "").lower() in ("yes", "y", "on", "true", "1") + if DOCKER: + print("Running inside docker container") + noip_username = os.environ.get('NOIP_USERNAME','') + noip_password = os.environ.get('NOIP_PASSWORD','') + noip_totp = os.environ.get('NOIP_2FA_SECRET_KEY','') + debug = int(os.environ.get('NOIP_DEBUG', 1)) + if len(noip_username) == 0 or len(noip_password) == 0 or len(noip_totp) == 0: sys.exit('You are using docker, you need to specify the required parameters as environment varialbes, check the documentation.') + else: + noip_username, noip_password, noip_totp, debug = get_args_values(argv) + return (Robot(noip_username, noip_password, noip_totp, debug, DOCKER)).run() def get_args_values(argv): if argv is None: argv = sys.argv - if len(argv) < 3: - print(f"Usage: {argv[0]} [] ") + if len(argv) < 4: + print(f"Usage: {argv[0]} <2FA_secret_key> [] ") sys.exit(1) noip_username = argv[1] noip_password = argv[2] + noip_totp = argv[3] debug = 1 - if len(argv) > 3: - debug = int(argv[3]) - return noip_username, noip_password, debug + if len(argv) > 4: + debug = int(argv[4]) + return noip_username, noip_password, noip_totp, debug if __name__ == "__main__": diff --git a/noip-renew.sh b/noip-renew.sh index d0001b9..ce9b9d9 100755 --- a/noip-renew.sh +++ b/noip-renew.sh @@ -2,13 +2,14 @@ USERNAME="" PASSWORD="" +TOTP_SECRET="" LOGDIR=$1 PROGDIR=$(dirname -- $0) if [ -z "$LOGDIR" ]; then - $PROGDIR/noip-renew.py "$USERNAME" "$PASSWORD" 2 + $PROGDIR/noip-renew.py "$USERNAME" "$PASSWORD" "$TOTP_SECRET" 2 else cd $LOGDIR - $PROGDIR/noip-renew.py "$USERNAME" "$PASSWORD" 0 >> $USERNAME.log + $PROGDIR/noip-renew.py "$USERNAME" "$PASSWORD" "$TOTP_SECRET" 0 >> $USERNAME.log fi diff --git a/setup.sh b/setup.sh index 2c0d687..975ae37 100755 --- a/setup.sh +++ b/setup.sh @@ -2,6 +2,7 @@ set -e PYTHON=python3 +PYTHON35=false USER=$(whoami) if [ "$USER" == "root" ]; then USER=$1 @@ -20,7 +21,7 @@ function config() { LOGDIR=/var/log/noip-renew/$USER INSTDIR=/usr/local/bin INSTEXE=$INSTDIR/noip-renew-$USER.sh - CRONJOB="0 1 * * * $USER $INSTEXE $LOGDIR" + CRONJOB="0 1 * * * $INSTEXE $LOGDIR" } function install() { @@ -34,20 +35,33 @@ function install() { install_debian ;; esac - # Debian9 package 'python-selenium' does not work with chromedriver, - # Install from pip, which is newer - $SUDO $PYTHON -m pip install selenium + + if [ "$PYTHON35" = true ]; then + $SUDO $PYTHON -m pip install future-fstrings + fi } function install_arch(){ $SUDO pacman -Qi cronie > /dev/null || $SUDO pacman -S cronie $SUDO pacman -Qi python > /dev/null || $SUDO pacman -S python $SUDO pacman -Qi python-pip > /dev/null || $SUDO pacman -S python-pip + $SUDO pacman -Qi python-pyotp > /dev/null || $SUDO pacman -S python-pyotp $SUDO pacman -Qi chromium > /dev/null || $SUDO pacman -S chromium + $SUDO $PYTHON -m pip install selenium } function install_debian(){ echo "Installing necessary packages..." + deb_arch=$(dpkg --print-architecture) + if [ "$deb_arch" == "amd64" ]; then + wget=/usr/bin/wget + if [ ! -x "$wget" ]; then + $SUDO apt -y install wget + fi + $SUDO sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list' + $SUDO sh -c 'wget -O- https://dl.google.com/linux/linux_signing_key.pub |gpg --dearmor > /etc/apt/trusted.gpg.d/google.gpg' + fi + read -p 'Perform apt-get update? (y/n): ' update if [ "${update^^}" = "Y" ] then @@ -61,14 +75,26 @@ function install_debian(){ $SUDO apt -y install cron PYV=`python3 -c "import sys;t='{v[0]}{v[1]}'.format(v=list(sys.version_info[:2]));sys.stdout.write(t)";` - if [[ "$PYV" -lt "36" ]] || ! hash python3; - then - echo "This script requires Python version 3.6 or higher. Attempting to install..." - $SUDO apt-get -y install python3 + if [[ "$PYV" -lt "36" ]] || ! hash python3; then + if [[ "$PYV" -eq "35" ]]; then + PYTHON35=true + else + echo "This script requires Python version 3.5 or higher. Attempting to install..." + $SUDO apt-get -y install python3 + fi fi - $SUDO apt -y install chromium-browser # Update Chromium Browser or script won't work. + $SUDO apt -y install chromium-browser || \ + $SUDO apt -y install chromium # Update Chromium Browser or script won't work. + $SUDO apt -y install $PYTHON-pip + $SUDO apt -y install $PYTHON-pyotp + + if [[ "$PYV" -gt "36" ]]; then + $SUDO apt -y install $PYTHON-selenium + else + $SUDO $PYTHON -m pip install selenium + fi } function deploy() { @@ -87,6 +113,11 @@ function deploy() { $SUDO chown $USER $INSTEXE $SUDO chown $USER $INSTDIR/noip-renew-skd.sh $SUDO chmod 700 $INSTEXE + + if [ "$PYTHON35" = true ]; then + $SUDO sed -i '2i # -*- coding: future_fstrings -*- ' $INSTDIR/noip-renew.py + fi + noip $SUDO crontab -u $USER -l | grep -v '/noip-renew*' | $SUDO crontab -u $USER - ($SUDO crontab -u $USER -l; echo "$CRONJOB") | $SUDO crontab -u $USER - @@ -97,15 +128,26 @@ function deploy() { } function noip() { - echo "Enter your No-IP Account details..." + echo "Enter your No-IP Account details...make sure you enabled 2fa authentication and saved the 2fa secret key" read -p 'Username: ' uservar read -sp 'Password: ' passvar + echo + read -sp '2FA Secret Key: ' totpsecret passvar=`echo -n $passvar | base64` echo $SUDO sed -i 's/USERNAME=".*"/USERNAME="'$uservar'"/1' $INSTEXE $SUDO sed -i 's/PASSWORD=".*"/PASSWORD="'$passvar'"/1' $INSTEXE + $SUDO sed -i 's/TOTP_SECRET=".*"/TOTP_SECRET="'$totpsecret'"/1' $INSTEXE + + read -p 'Do you want randomized cronjob? (y/n): ' rcron + if [ "${rcron^^}" = "Y" ] + then + read -p 'Enter time interval (hours): ' tint + $SUDO sed -i '2 c Min=$(/usr/bin/shuf -i 0-59 -n 1)' $INSTDIR/noip-renew-skd.sh + $SUDO sed -i '3 c Hour=$(/usr/bin/shuf -i '$tint' -n 1)' $INSTDIR/noip-renew-skd.sh + fi } function installer() { @@ -121,6 +163,7 @@ function uninstall() { if [ "${clearLogs^^}" = "Y" ] then $SUDO rm -rf $LOGDIR + $SUDO crontab -u $USER -l | grep -v '/noip-renew*' | $SUDO crontab -u $USER - fi }