diff --git a/roles/2-common/tasks/packages.yml b/roles/2-common/tasks/packages.yml
index 9331ea69a..fc2890816 100644
--- a/roles/2-common/tasks/packages.yml
+++ b/roles/2-common/tasks/packages.yml
@@ -33,7 +33,7 @@
state: present
when: is_debuntu | bool
-- name: "Install 23 common packages: acpid, bridge-utils, bzip2, curl, gawk, hostapd, htop, i2c-tools, logrotate, make, mlocate, netmask, net-tools, ntfs-3g, pandoc, pastebinit, rsync, sudo, tar, unzip, usbmount, usbutils, wget"
+- name: "Install 24 common packages: acpid, bridge-utils, bzip2, curl, gawk, hostapd, htop, i2c-tools, logrotate, make, mlocate, netmask, net-tools, ntfs-3g, pandoc, pastebinit, rsync, sqlite3,sudo, tar, unzip, usbmount, usbutils, wget"
package:
name:
- acpid
@@ -56,6 +56,7 @@
- pandoc
- pastebinit
- rsync
+ - sqlite3
- sudo
- tar
- unzip
diff --git a/roles/9-local-addons/tasks/main.yml b/roles/9-local-addons/tasks/main.yml
index 4837c55e0..9f2c3b40f 100644
--- a/roles/9-local-addons/tasks/main.yml
+++ b/roles/9-local-addons/tasks/main.yml
@@ -18,9 +18,9 @@
# To be ported soon
- name: CAPTIVE PORTAL
- include_tasks: roles/captive-portal/tasks/main.yml
- when: captive_portal_install | bool
- tags: base, captive-portal, network, domain
+ include_tasks: roles/captiveportal/tasks/main.yml
+ when: captiveportal_install | bool
+ tags: base, captiveportal, network, domain
- name: MINETEST
include_role:
diff --git a/roles/captive-portal/tasks/main.yml b/roles/captive-portal/tasks/main.yml
deleted file mode 100644
index 569afb869..000000000
--- a/roles/captive-portal/tasks/main.yml
+++ /dev/null
@@ -1,153 +0,0 @@
-- name: Download & install python-dateutil, sqlite3
- package:
- name: "{{ item }}"
- state: present
- with_items:
- - python-dateutil
- - sqlite3 # @georgejhunt hopes to move this to 2-common (or more likely 3-base-server, alongside MySQL) in October 2018
-
-- name: Install libapache2-mod-wsgi (debuntu)
- package:
- name: libapache2-mod-wsgi
- state: present
- when: is_debuntu | bool
-
-- name: Install mod_wsgi (not debuntu)
- package:
- name: mod_wsgi
- state: present
- when: not is_debuntu
-
-- name: Create directory /opt/iiab/captive-portal for scripts & templates
- file:
- path: /opt/iiab/captive-portal
- state: directory
- owner: "{{ apache_user }}"
-
-- name: 'Copy scripts: checkurls, capture-wsgi.py'
- template:
- src: "{{ item.src }}"
- dest: /opt/iiab/captive-portal/
- mode: "{{ item.mode }}"
- with_items:
- - { src: roles/captive-portal/templates/checkurls, mode: '0644' }
- - { src: roles/captive-portal/templates/capture-wsgi.py, mode: '0755' }
-
-- name: 'Copy templates: simple.template, mac.template'
- copy:
- src: "{{ item }}"
- dest: /opt/iiab/captive-portal/
- with_items:
- - roles/captive-portal/files/simple.template
- - roles/captive-portal/files/mac.template
-
-- name: Copy iiab-catch & iiab-uncatch into /usr/bin/
- template:
- src: "{{ item }}"
- dest: /usr/bin/
- owner: root
- group: root
- mode: 0755
- with_items:
- - roles/captive-portal/templates/iiab-catch
- - roles/captive-portal/templates/iiab-uncatch
-
-- name: Run iiab-uncatch to generate diversion lists for dnsmasq and apache2
- shell: /usr/bin/iiab-uncatch
-
-#- name: Install systemd unit file captive-portal.service from template
-# template:
-# src: roles/captive-portal/templates/captive-portal.service.j2
-# dest: /etc/systemd/system/captive-portal.service
-# owner: root
-# group: root
-# mode: 0644
-
-- name: Install Apache's captive-portal.conf from template if captive_portal_enabled
- template:
- src: roles/captive-portal/templates/001-captive-portal.conf
- dest: /etc/{{ apache_config_dir }}/001-captive-portal.conf
- owner: root
- group: root
- mode: 0644
- when: captive_portal_enabled | bool
-
-- name: Enable Apache's captive-portal.conf if captive_portal_enabled (debuntu)
- file:
- src: /etc/apache2/sites-available/001-captive-portal.conf
- path: /etc/apache2/sites-enabled/001-captive-portal.conf
- state: link
- when: captive_portal_enabled and is_debuntu
-
-- name: Enable Apache's default-ssl.conf if captive_portal_enabled (debuntu)
- file:
- src: /etc/apache2/sites-available/default-ssl.conf
- path: /etc/apache2/sites-enabled/default-ssl.conf
- state: link
- when: captive_portal_enabled and is_debuntu
-
-#- name: Enable & Start systemd service captive-portal.service if captive_portal_enabled
-# systemd:
-# name: captive-portal.service
-# daemon-reload: yes
-# enabled: yes
-# state: started
-# when: captive_portal_enabled | bool
-
-#- name: Disable & Stop captive-portal.service if not captive_portal_enabled
-# systemd:
-# name: captive-portal.service
-# enabled: no
-# state: stopped
-# when: not captive_portal_enabled
-
-- name: Disable Apache's captive-portal.conf if not captive_portal_enabled (debuntu)
- file:
- path: /etc/apache2/sites-enabled/001-captive-portal.conf
- state: absent
- when: not captive_portal_enabled and is_debuntu
-
-- name: Disable Apache's default-ssl.conf if not captive_portal_enabled (debuntu)
- file:
- path: /etc/apache2/sites-enabled/default-ssl.conf
- state: absent
- when: not captive_portal_enabled and is_debuntu
-
-- name: Make sure dnsmasq is not diverting if not captive_portal_enabled
- file:
- path: /etc/dnsmasq.d/capture
- state: absent
- when: not captive_portal_enabled
-
-- name: Add 'captive_portal_installed' variable values to {{ iiab_state_file }}
- lineinfile:
- dest: "{{ iiab_state_file }}"
- regexp: '^captive_portal_installed'
- line: 'captive_portal_installed: True'
- state: present
-
-- name: Restart Apache service ({{ apache_service }}) # i.e. apache2 on most distros
- systemd:
- name: "{{ apache_service }}"
- state: restarted
-
-#- name: Restart dnsmasq
-# systemd:
-# name: dnsmasq
-# state: restarted
-# when: dnsmasq_enabled | bool
-
-# ABOVE DOES NOT WORK ON UBUNTU 16.04 -- what follows is a crude hack (seems to work!)
-
-- name: Stop dnsmasq
- systemd:
- name: dnsmasq
- state: stopped
- when: dnsmasq_enabled | bool
-
-- name: Start dnsmasq
- systemd:
- name: dnsmasq
- state: started
- when: dnsmasq_enabled | bool
-
diff --git a/roles/captive-portal/templates/001-captive-portal.conf b/roles/captive-portal/templates/001-captive-portal.conf
deleted file mode 100644
index 2890c179f..000000000
--- a/roles/captive-portal/templates/001-captive-portal.conf
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
{{ btn1 }}
diff --git a/roles/captiveportal/tasks/main.yml b/roles/captiveportal/tasks/main.yml new file mode 100644 index 000000000..30c84f477 --- /dev/null +++ b/roles/captiveportal/tasks/main.yml @@ -0,0 +1,117 @@ +- name: Download & install python-dateutil, sqlite3 + package: + name: "{{ item }}" + state: present + with_items: + - python3-dateutil + - python3-jinja2 + +- name: Create directory /opt/iiab/captiveportal for scripts & templates + file: + path: /opt/iiab/captiveportal + state: directory + owner: "{{ apache_user }}" + +- name: 'Copy scripts: checkurls' + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: "{{ item.mode }}" + with_items: + - { src: roles/captiveportal/templates/checkurls, mode: '0644', dest: /opt/iiab/captiveportal/ } + - { src: roles/captiveportal/templates/iiab-make-cp-servers.py, mode: '0755', dest: /usr/sbin/ } + - { src: roles/captiveportal/templates/iiab-divert-to-nginx, mode: '0755', dest: /usr/sbin/ } + +- name: Put put the python script that creates the server in place + template: + src: roles/captiveportal/templates/capture-wsgi.py + mode: '0755' + dest: /opt/iiab/captiveportal/ + +- name: 'Copy templates: simple.template, mac.template' + copy: + src: "{{ item }}" + dest: /opt/iiab/captiveportal/ + with_items: + - roles/captiveportal/files/simple.template + - roles/captiveportal/files/mac.template + +- name: Copy uWSGI config file + template: + src: roles/captiveportal/templates/captiveportal.ini.j2 + dest: /opt/iiab/captiveportal/captiveportal.ini + +- name: Copy unit file for uWSGI service + template: + src: roles/captiveportal/templates/uwsgi-captiveportal.service + dest: /etc/systemd/system/ + +- name: Start or restart server which responds to browsers trying to detect a captive portal + systemd: + name: uwsgi-captiveportal.service + state: restarted + enabled: True + when: captiveportal_enabled | bool + +- name: Stop uWSGI server if captive portal has been disabled + systemd: + name: uwsgi-captiveportal.service + state: stopped + enabled: False + when: not captiveportal_enabled | bool + +- name: Run divert to generate diversion lists for nginx + shell: /usr/sbin/iiab-divert-to-nginx + +- name: Run script to generate nginx servers from checkurls input list + command: /usr/sbin/iiab-make-cp-servers.py + args: + creates: /etc/nginx/sites-available/capture.conf + +- name: Enable nginx to service the sites in checkurls list + file: + src: /etc/nginx/sites-available/capture.conf + path: /etc/nginx/sites-enabled/capture.conf + state: link + when: captiveportal_enabled | bool + +- name: Disable nginx to location definitions for checkurls + file: + src: /etc/nginx/sites-available/capture.conf + path: /etc/nginx/sites-enabled/capture.conf + state: absent + when: not captiveportal_enabled | bool + +- name: Make sure dnsmasq is not diverting if not captiveportal_enabled + file: + path: /etc/dnsmasq.d/capture + state: absent + when: not captiveportal_enabled + +- name: Add 'captiveportal_installed' variable values to {{ iiab_state_file }} + lineinfile: + dest: "{{ iiab_state_file }}" + regexp: '^captiveportal_installed' + line: 'captiveportal_installed: True' + state: present + +#- name: Restart dnsmasq +# systemd: +# name: dnsmasq +# state: restarted +# when: dnsmasq_enabled | bool + +# ABOVE DOES NOT WORK ON UBUNTU 16.04 -- what follows is a crude hack (seems to work!) + +- name: Stop dnsmasq + systemd: + name: dnsmasq + state: stopped + when: dnsmasq_enabled | bool + +- name: Start dnsmasq + systemd: + name: dnsmasq + state: started + when: dnsmasq_enabled | bool + diff --git a/roles/captiveportal/templates/captiveportal.ini.j2 b/roles/captiveportal/templates/captiveportal.ini.j2 new file mode 100644 index 000000000..72c9778fc --- /dev/null +++ b/roles/captiveportal/templates/captiveportal.ini.j2 @@ -0,0 +1,9 @@ +[uwsgi] + uid = {{ apache_user }} + gid = {{ apache_user }} + http-socket = :{{ captiveportal_port }} + chdir = /opt/iiab/captiveportal + wsgi-file = capture-wsgi.py + master = true + plugins = python3 + py-autoreload = 2 diff --git a/roles/captive-portal/templates/capture-wsgi.py b/roles/captiveportal/templates/capture-wsgi.py similarity index 80% rename from roles/captive-portal/templates/capture-wsgi.py rename to roles/captiveportal/templates/capture-wsgi.py index 4dc61ba43..bcfa466a9 100755 --- a/roles/captive-portal/templates/capture-wsgi.py +++ b/roles/captiveportal/templates/capture-wsgi.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 # -*- coding: utf-8 -*- # using Python's bundled WSGI server @@ -13,6 +13,7 @@ import sys from jinja2 import Environment, FileSystemLoader import sqlite3 import re +from iiab.iiab_lib import get_iiab_env # Notes on timeout strategy # every client timestamp is recorded into current_ts @@ -23,7 +24,7 @@ import re # # Create the jinja2 environment. -CAPTIVE_PORTAL_BASE = "/opt/iiab/captive-portal" +CAPTIVE_PORTAL_BASE = "/opt/iiab/captiveportal" j2_env = Environment(loader=FileSystemLoader(CAPTIVE_PORTAL_BASE),trim_blocks=True) # Define time outs @@ -34,61 +35,33 @@ PORTAL_TO = 20 # delay after triggered by ajax upon click of link to home page # Get the IIAB variables -sys.path.append('/etc/iiab/') -from iiab_env import get_iiab_env doc_root = get_iiab_env("WWWROOT") fully_qualified_domain_name = get_iiab_env("FQDN") +loggingLevel = "ERROR" +#loggingLevel = "DEBUG" +if len(sys.argv) > 1: + if sys.argv[1] == '-l': + loggingLevel = "DEBUG" + # set up some logging -- selectable for diagnostics -# Create dummy iostream to capture stderr and stdout -class StreamToLogger(object): - """ - Fake file-like stream object that redirects writes to a logger instance. - """ - def __init__(self, logger, log_level=logging.INFO): - self.logger = logger - self.log_level = log_level - self.linebuf = '' - - def write(self, buf): - for line in buf.rstrip().splitlines(): - self.logger.log(self.log_level, line.rstrip()) - -#if len(sys.argv) > 1 and sys.argv[1] == '-l': -if True: - loggingLevel = logging.DEBUG - try: - os.remove('/var/log/apache2/portal.log') - except: - pass -else: - loggingLevel = logging.ERROR - -# divert stdout and stderr to logger logging.basicConfig(filename='/var/log/apache2/portal.log',format='%(asctime)s.%(msecs)03d:%(name)s:%(message)s', datefmt='%M:%S',level=loggingLevel) logger = logging.getLogger('/var/log/apache2/portal.log') handler = RotatingFileHandler("/var/log/apache2/portal.log", maxBytes=100000, backupCount=2) logger.addHandler(handler) -stdout_logger = logging.getLogger('STDOUT') -sl = StreamToLogger(stdout_logger, logging.ERROR) -sys.stdout = sl - -stderr_logger = logging.getLogger('STDERR') -sl = StreamToLogger(stderr_logger, logging.ERROR) -sys.stderr = sl -PORT={{ captive_portal_port }} +PORT={{ captiveportal_port }} +#PORT=9090 # Define globals -ANDROID_TRIGGERED=False logger.debug("") logger.debug('##########################################') # what language are we speaking? lang = os.environ['LANG'][0:2] -logger.debug('speaking: %s'%lang) +logger.debug('speaking: {}'.format(lang)) def tstamp(dtime): '''return a UNIX style seconds since 1970 for datetime input''' @@ -141,8 +114,7 @@ def timeout_info(ip): def is_inactive(ip): ts=tstamp(datetime.datetime.now(tzutc())) current_ts, last_ts, send204after = timeout_info(ip) - logger.debug("In is_inactive. current_ts:%s. last_ts:%s. send204after:%s"%\ - (current_ts,last_ts,send204after,)) + logger.debug("In is_inactive. current_ts:{}. last_ts:{}. send204after:{}".format(current_ts,last_ts,send204after,)) if not last_ts: return True if ts - int(last_ts) > INACTIVITY_TO: @@ -154,7 +126,7 @@ def is_after204_timeout(ip): ts=tstamp(datetime.datetime.now(tzutc())) current_ts, last_ts, send204after = timeout_info(ip) if send204after == 0: return False - logger.debug("function: is_after204_timeout send204after:%s current: %s"%(send204after,ts,)) + logger.debug("function: is_after204_timeout send204after:{} current: {}".format(send204after,ts,)) if not send204after: return False if ts - int(send204after) > 0: @@ -163,12 +135,10 @@ def is_after204_timeout(ip): return False def set_204after(ip,value): - global ANDROID_TRIGGERED ts=tstamp(datetime.datetime.now(tzutc())) sql = 'UPDATE users SET send204after = ? where ip = ?' c.execute(sql,(ts + value,ip,)) conn.commit() - ANDROID_TRIGGERED = False def set_lasttimestamp(ip): ts=tstamp(datetime.datetime.now(tzutc())) @@ -178,31 +148,33 @@ def set_lasttimestamp(ip): # ################### Action routines based on OS ################3 def microsoft(environ,start_response): + logger.debug('in microsoft') # firefox -- seems both mac and Windows use it agent = environ.get('HTTP_USER_AGENT','default_agent') if agent.startswith('Mozilla'): + logger.debug("sending microsoft redirect for agent Mozilla") return home(environ, start_response) - logger.debug("sending microsoft redirect") - response_body = "" + response_body = b"" status = '302 Moved Temporarily' - response_headers = [('Location','http://box.lan/home'), + response_headers = [('Location','http://' + fully_qualified_domain_name + '{{ captiveportal_splash_page }}'), ('Content-type','text/html'), ('Content-Length',str(len(response_body)))] start_response(status, response_headers) + logger.debug("redirect to home. Status: %s Headers: %s"%(status,repr(response_headers))) return [response_body] def home(environ,start_response): logger.debug("sending direct to home") - response_body = "" + response_body = b"" status = '302 Moved Temporarily' - response_headers = [('Location','http://' + fully_qualified_domain_name + '/home'), + response_headers = [('Location','http://' + fully_qualified_domain_name + '{{ captiveportal_splash_page }}'), ('Content-type','text/html'), ('Content-Length',str(len(response_body)))] start_response(status, response_headers) + logger.debug("redirect to home. Status: %s Headers: %s"%(status,repr(response_headers))) return [response_body] def android(environ, start_response): - global ANDROID_TRIGGERED if environ.get('HTTP_X_FORWARDED_FOR'): ip = environ['HTTP_X_FORWARDED_FOR'].strip() else: @@ -211,16 +183,16 @@ def android(environ, start_response): if system_version is None: return put_302(environ, start_response) if system_version[0:1] < '6': - logger.debug("system < 6:%s"%system_version) + logger.debug("system < 6:{}".format(system_version)) location = '/android_splash' set_204after(ip,0) elif system_version[:1] >= '7': - location = "http://" + fully_qualified_domain_name + "/home" + location = "http://" + fully_qualified_domain_name + '{{ captiveportal_splash_page }}' else: #set_204after(ip,20) location = '/android_https' agent = environ.get('HTTP_USER_AGENT','default_agent') - response_body = "hello" + response_body = b"hello" status = '302 Moved Temporarily' response_headers = [('Location',location)] start_response(status, response_headers) @@ -229,10 +201,10 @@ def android(environ, start_response): def android_splash(environ, start_response): en_txt={ 'message':"Click on the button to go to the IIAB home page",\ 'btn1':"GO TO IIAB HOME PAGE", \ - "FQDN": fully_qualified_domain_name, \ + "FQDN": fully_qualified_domain_name + '{{ captiveportal_splash_page }}', \ 'doc_root':get_iiab_env("WWWROOT") } es_txt={ 'message':"Haga clic en el botón para ir a la página de inicio de IIAB",\ - "FQDN": fully_qualified_domain_name, \ + "FQDN": fully_qualified_domain_name + '{{ captiveportal_splash_page }}', \ 'btn1':"IIAB",'doc_root':get_iiab_env("WWWROOT")} txt = en_txt if lang == "en": @@ -240,6 +212,7 @@ def android_splash(environ, start_response): elif lang == "es": txt = es_txt response_body = str(j2_env.get_template("simple.template").render(**txt)) + response_body = response_body.encode() status = '200 OK' response_headers = [('Content-type','text/html'), ('Content-Length',str(len(response_body)))] @@ -250,10 +223,10 @@ def android_https(environ, start_response): en_txt={ 'message':"""Please ignore the SECURITY warning which appears after clicking the first button""",\ 'btn2':'Click this first Go to the browser we need',\ 'btn1':'Then click this to go to IIAB home page',\ - "FQDN": fully_qualified_domain_name, \ + "FQDN": fully_qualified_domain_name + '{{ captiveportal_splash_page }}', \ 'doc_root':get_iiab_env("WWWROOT") } es_txt={ 'message':"Haga clic en el botón para ir a la página de inicio de IIAB",\ - "FQDN": fully_qualified_domain_name, \ + "FQDN": fully_qualified_domain_name + '{{ captiveportal_splash_page }}', \ 'btn1':"IIAB",'doc_root':get_iiab_env("WWWROOT")} txt = en_txt if lang == "en": @@ -261,6 +234,7 @@ def android_https(environ, start_response): elif lang == "es": txt = es_txt response_body = str(j2_env.get_template("simple.template").render(**txt)) + response_body = response_body.encode() status = '200 OK' response_headers = [('Content-type','text/html'), ('Content-Length',str(len(response_body)))] @@ -268,13 +242,14 @@ def android_https(environ, start_response): return [response_body] def mac_splash(environ,start_response): + logger.debug('in mac_splash') logger.debug("in function mac_splash") - en_txt={ 'message':"Click on the button to go to the IIAB home page",\ - 'btn1':"GO TO IIAB HOME PAGE",'success_token': 'Success', - "FQDN": fully_qualified_domain_name, \ + en_txt={ 'message': "Click on the button to go to the IIAB home page",\ + 'btn1': "GO TO IIAB HOME PAGE",'success_token': 'Success', + "FQDN": fully_qualified_domain_name + '{{ captiveportal_splash_page }}', \ 'doc_root':get_iiab_env("WWWROOT")} es_txt={ 'message':"Haga clic en el botón para ir a la página de inicio de IIAB",\ - "FQDN": fully_qualified_domain_name, \ + "FQDN": fully_qualified_domain_name + '{{ captiveportal_splash_page }}', \ 'btn1':"IIAB",'doc_root':get_iiab_env("WWWROOT")} txt = en_txt if lang == "en": @@ -283,6 +258,7 @@ def mac_splash(environ,start_response): txt = es_txt set_lasttimestamp(ip) response_body = str(j2_env.get_template("mac.template").render(**txt)) + response_body = response_body.encode() status = '200 Success' response_headers = [('Content-type','text/html'), ('Content-Length',str(len(response_body)))] @@ -290,6 +266,7 @@ def mac_splash(environ,start_response): return [response_body] def macintosh(environ, start_response): + logger.debug('in macintosh') global ip logger.debug("in function mcintosh") #print >> sys.stderr , "Geo Print to stderr" + environ['HTTP_HOST'] @@ -302,6 +279,7 @@ def macintosh(environ, start_response): response_body = """