1
0
Fork 0
mirror of https://github.com/iiab/iiab.git synced 2025-03-09 15:40:17 +00:00

add in template dir

rebase bassed upon copy in

cut out obvious dead code

working on put-204

make users a sqlite db

sqlite db has users, and agent info

android timeouts not yet working

android 5 and 6 both work. lost mac

return to a working version for the MAC. Missing the splash

android,mac,windows all appear to work

sqlite get status of execute row == Null

initialize lasttimestamp with ajax call when home is triggered

remove commented code, move towards logging vs print statements

add logging with the -l flag

no changes to default_vars.yml

drop iptables captive portal stuff not using port 8090, and dnsmasq

missed deleting trap_enabled

fixes for 6.7 defaults

add in template dir

rebase bassed upon copy in

cut out obvious dead code

working on put-204

make users a sqlite db

sqlite db has users, and agent info

android timeouts not yet working

android 5 and 6 both work. lost mac

return to a working version for the MAC. Missing the splash

android,mac,windows all appear to work

sqlite get status of execute row == Null

initialize lasttimestamp with ajax call when home is triggered

remove commented code, move towards logging vs print statements

drop iptables captive portal stuff not using port 8090, and dnsmasq

missed deleting trap_enabled

fixes for 6.7 defaults

dispense with apache logs for captive portal, use the rotating portal.log instead

bring in clean defaults and py

Squash debugging details

remove backup file

still cannot dispense with cna on iphone. mac escape from cna broke with these changes

captive comes after iiab in apache config

one filename wrong

logging used for debug, lost mac escape from cna

typos

got mac/iphone full browser back

remove dead code

python was not creating db, or putting ip when first encountered
This commit is contained in:
George Hunt 2018-08-24 00:26:20 +00:00
parent 49a0523074
commit 4ee9c3e2b5
16 changed files with 897 additions and 38 deletions

Binary file not shown.

View file

@ -4,6 +4,8 @@ IIAB_BASE_PATH={{ iiab_base }}
IIAB_DIR={{ iiab_dir }}
IIAB_RELEASE={{ iiab_base_ver }}
IIAB_REVISION={{ iiab_revision }}
IIAB_GATEWAY_ENABLED={{ iiab_gateway_enabled }}
LAN_IP={{ lan_ip }}
OS={{ ansible_local.local_facts.os }}
OS_VER={{ ansible_local.local_facts.os_ver }}
WWWROOT={{ doc_root }}

View file

@ -159,8 +159,6 @@ UseCanonicalName Off
# so it doesn't move in some future version.
#
DocumentRoot "{{ doc_root }}"
ErrorLog {{ apache_log_dir }}/error.log
CustomLog {{ apache_log_dir }}/access.log combined
#
# Each directory to which Apache has access can be configured with respect
@ -175,12 +173,17 @@ CustomLog {{ apache_log_dir }}/access.log combined
AllowOverride None
</Directory>
<VirtualHost *:80>
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
ServerName box
ServerAlias box.lan
<Directory "{{ doc_root }}">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
#
# UserDir: The name of the directory that is appended onto a user's home
# directory if a ~user request is received.

View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=.75">
<title>Success</title>
<style type="text/css">
@media (min-width: 501px){
#header {
display: block;
height: 120px;
width:1024px;
background: #000 url('iiab_banner6.png') no-repeat 0 0;
border-radius: 5px;
margin: 5px;
}
body {
background-color: #CBFFAA;
font-family: sans-serif;
font-size: 100%;
width: 1024px;
margin: 3px;
}
}
.button {
font: bold 14px Arial;
text-decoration: none;
background-color: #EEEEEE;
color: #333333;
padding: 2px 6px 2px 6px;
border-top: 1px solid #CCCCCC;
border-right: 1px solid #333333;
border-bottom: 1px solid #333333;
border-left: 1px solid #CCCCCC;
radius: 5px;
}
</style>
</head>
<body>
Success
<br>
<div align="center">
<div id="header"></div>
<H1>Welcome to IIAB</H1>
<br><br>
{{ message }}
<div id="choices">
<br><br>
<br><br>
<br><br>
<br><br>
<a class="button" href="http://box.lan/home">{{ btn1 }}</a>
</div>
</body>
</html>

View file

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<title>Success</title>
<meta name="viewport" content="width=device-width, initial-scale=.75">
<script src="/jquery.min.js"></script>
<!-- <link rel="stylesheet" href="/bootstrap.min.css"/>
<script src="/bootstrap.min.js"></script> -->
<style type="text/css">
@media (max-width: 500px){
#header {
display: block;
height: 120px;
width:350px;
background: #000 url('iiab_banner6.png') no-repeat 0 0;
border-radius: 5px;
margin: 5px;
}
body {
background-color: #CBFFAA;
font-family: sans-serif;
font-size: 100%;
width: 350px;
margin: 3px;
}
}
@media (min-width: 501px){
#header {
display: block;
height: 120px;
width:1024px;
background: #000 url('iiab_banner6.png') no-repeat 0 0;
border-radius: 5px;
margin: 5px;
}
body {
background-color: #CBFFAA;
font-family: sans-serif;
font-size: 100%;
width: 1024px;
margin: 3px;
}
}
a {
color: #249;
text-decoration: none;
outline: none; /* don't outline image links (ff) */
}
.content-item {
padding: 10px 0 10px 0;
border: 1px solid #aaa;
border-radius: 5px;
margin-bottom: 2px;
background-color: #fff;
}
.button {
font: bold 14px Arial;
text-decoration: none;
background-color: #EEEEEE;
color: #333333;
padding: 2px 6px 2px 6px;
border-top: 1px solid #CCCCCC;
border-right: 1px solid #333333;
border-bottom: 1px solid #333333;
border-left: 1px solid #CCCCCC;
radius: 5px;
}
</style>
<script>
var w = window.innerWidth;
function homeclick(){
window.open("http://box.lan/home","_system");
$.ajax("/home_selected");
}
</script>
</head>
<body>
{% if success_token is defined %}
Success
{% endif %}
<br>
<div align="center">
<div id="header"></div>
<H1>Welcome to IIAB</H1>
<br><br>
{{ message }}
<div id="choices">
{% if btn2 is defined %}
<a class="button" href="https://captive.lan/android_splash">{{ btn2 }}</a>
{% endif %}
<br><br>
<br><br>
<br><br>
<br><br>
<!-- <a class="button" href="http://box.lan/home">{{ btn1 }}</a> -->
<a class="button" onclick="homeclick()">{{ btn1 }}</a>
</div>
</body>
</html>

View file

@ -1,3 +1,9 @@
- name: Get python dateutil
package:
name: python-dateutil
state: present
when: py_captive_portal_install
- name: Create directory for Captive Portal script
file:
path: /opt/iiab/captive-portal
@ -6,42 +12,105 @@
- name: Copy Captive Portal script
template:
src: roles/network/templates/captive_portal/captive_portal.py.j2
dest: /opt/iiab/captive-portal/captive_portal.py
owner: root
group: root
mode: 0740
src: "{{ item.src }}"
dest: /opt/iiab/captive-portal/
mode: "{{ item.mode }}"
with_items:
- { src: roles/network/templates/captive-portal/checkurls, mode: '0644' }
- { src: roles/network/templates/captive-portal/capture-wsgi.py, mode: '0755' }
when: py_captive_portal_install
- name: Copy the jinja2 template to captive portal
copy:
src: roles/network/files/simple.template
dest: /opt/iiab/captive-portal/
- name: Copy the jinja2 template to captive portal
copy:
src: roles/network/files/mac.template
dest: /opt/iiab/captive-portal/
- name: Copy Captive Portal scripts
template:
src: "{{ item.src }}"
dest: /usr/bin/
owner: root
group: root
mode: 0755
with_items:
- src: roles/network/templates/captive-portal/iiab-catch
- src: roles/network/templates/captive-portal/iiab-uncatch
when: py_captive_portal_install
- name: Generate the diversion lists for dnsmasq and apache2
shell: /usr/bin/iiab-uncatch
when: py_captive_portal_install
when: py_captive_portal_install
- name: Copy Captive Portal service file
template:
src: roles/network/templates/captive_portal/captive_portal.service.j2
dest: /etc/systemd/system/captive_portal.service
src: roles/network/templates/captive-portal/captive-portal.service.j2
dest: /etc/systemd/system/captive-portal.service
owner: root
group: root
mode: 0644
when: py_captive_portal_install
- name: Enable captive_portal after copying files
- name: Copy Captive Portal Apache config file
template:
src: roles/network/templates/captive-portal/captive-portal.conf
dest: /etc/{{ apache_config_dir }}/captive-portal.conf
owner: root
group: root
mode: 0740
when: py_captive_portal_install and py_captive_portal_enabled
- name: Enable captive-portal after copying files
service:
name: captive_portal.service
name: captive-portal.service
enabled: yes
when: py_captive_portal_install and py_captive_portal_enabled
- name: Start captive_portal after copying files
- name: Enable Apache config file
file:
src: /etc/apache2/sites-available/captive-portal.conf
dest: /etc/apache2/sites-enabled/captive-portal.conf
state: link
when: py_captive_portal_enabled and is_debuntu
- name: Enable Apache ssl config file
file:
src: /etc/apache2/sites-available/default-ssl.conf
dest: /etc/apache2/sites-enabled/default-ssl.conf
state: link
when: py_captive_portal_enabled and is_debuntu
- name: Start captive-portal after copying files
service:
name: captive_portal.service
name: captive-portal.service
state: started
when: py_captive_portal_install and py_captive_portal_enabled
- name: Disable captive_portal after copying files
- name: Disable captive-portal after copying files
service:
name: captive_portal.service
name: captive-portal.service
enabled: no
when: py_captive_portal_install and py_captive_portal_enabled
when: py_captive_portal_install and not py_captive_portal_enabled
- name: Stop captive_portal after copying files
- name: Stop captive-portal after copying files
service:
name: captive_portal.service
state: started
when: py_captive_portal_install and py_captive_portal_enabled
name: captive-portal.service
state: stopped
when: py_captive_portal_install and not py_captive_portal_enabled
- name: Disable Apache config file
file:
dest: /etc/apache2/sites-enabled/captive-portal.conf
state: absent
when: not py_captive_portal_enabled and is_debuntu
- name: Make sure dnsmasq is not diverting if captive-portal disabled
file:
dest: /etc/dnsmasq.d/capture
state: absent
when: not py_captive_portal_enabled

View file

@ -15,7 +15,7 @@
- name: Configure fqdn in /etc/hosts appliance mode
lineinfile: dest=/etc/hosts
regexp='^127\.0\.0\.1'
line='127.0.0.1 localhost.localdomain localhost {{ iiab_hostname }}.{{ iiab_domain }} {{ iiab_hostname }} box '
line='127.0.0.1 localhost.localdomain localhost'
owner=root
group=root
mode=0644

View file

@ -0,0 +1,14 @@
<VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
ServerName iiab.io
Include /etc/apache2/capture
ProxyPreserveHost On
ProxyPass / http://box.lan:{{ py_captive_portal_port }}/
ProxyPassReverse / http://box.lan:{{ py_captive_portal_port }}/
</VirtualHost>

View file

@ -7,7 +7,7 @@ Type=simple
User=root
Group=root
WorkingDirectory=/opt/iiab/captive-portal
ExecStart=/opt/iiab/captive-portal/captive_portal.py
ExecStart=/opt/iiab/captive-portal/capture-wsgi.py
StandardOutput=syslog
StandardError=syslog

View file

@ -0,0 +1,550 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# using Python's bundled WSGI server
from wsgiref.simple_server import make_server
import subprocess
from dateutil.tz import *
import datetime
import logging
from logging.handlers import RotatingFileHandler
import os
import sys
from jinja2 import Environment, FileSystemLoader
import sqlite3
import re
# Notes on timeout strategy
# every client timestamp is recorded into current_ts
# When splash page is clicked , return 204 timeout starts (via ajax call),
# Return 204 is android (may be different for different versions)
# captive portal redirect is triggered after inactivity timeout,
# which needs to be longer than period of normal connecetivity checks by OS
#
# Create the jinja2 environment.
CAPTIVE_PORTAL_BASE = "/opt/iiab/captive-portal"
j2_env = Environment(loader=FileSystemLoader(CAPTIVE_PORTAL_BASE),trim_blocks=True)
# Define time outs
INACTIVITY_TO = 30
PORTAL_TO = 0 # delay after triggered by ajax upon click of link to home page
# I had hoped that returning 204 status after some delay
# would dispense with android's "sign-in to network" (no work)
# Get the IIAB variables
sys.path.append('/etc/iiab/')
from iiab_env import get_iiab_env
doc_root = get_iiab_env("WWWROOT")
# make a way to find new URLs queried by new clients
# CATCH substitues this server for apache at port 80
CATCH = False
if len(sys.argv) > 1 and sys.argv[1] == '-d':
CATCH = True
PORT=80
else:
PORT=9090
# 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':
loggingLevel = logging.DEBUG
else:
loggingLevel = logging.ERROR
loggingLevel = logging.DEBUG
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)
# divert stdout and stderr to logger
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
# Define globals
MAC_SUCCESS=False
ANDROID_TRIGGERED=False
logger.debug("")
logger.debug('##########################################')
# what language are we speaking?
lang = os.environ['LANG'][0:2]
logger.debug('speaking: %s'%lang)
def tstamp(dtime):
'''return a UNIX style seconds since 1970 for datetime input'''
epoch = datetime.datetime(1970, 1, 1,tzinfo=tzutc())
newdtime = dtime.astimezone(tzutc())
since_epoch_delta = newdtime - epoch
return since_epoch_delta.total_seconds()
# ##########database operations ##############
# Use a sqlite database to store per client information
user_db = os.path.join(CAPTIVE_PORTAL_BASE,"users.sqlite")
conn = sqlite3.connect(user_db)
if not os.path.exists(user_db):
conn.close()
conn = sqlite3.connect(user_db)
c = conn.cursor()
c.row_factory = sqlite3.Row
c.execute( """create table IF NOT EXISTS users
(ip text PRIMARY KEY, mac text, current_ts integer,
lasttimestamp integer, send204after integer,
os text, os_version text,
ymd text)""")
def update_user(ip, mac, system, system_version, ymd):
sql = "SELECT * FROM users WHERE ip = ?"
c.execute(sql,(ip,))
row = c.fetchone()
if row == None:
sql = "INSERT INTO users (ip,mac,os,os_version,ymd) VALUES (?,?,?,?,?)"
c.execute(sql,(ip, mac, system, system_version, ymd ))
else:
sql = "UPDATE users SET (mac,os,os_version,ymd) = ( ?, ?, ?, ? ) WHERE ip = ?"
c.execute(sql,(mac, system, system_version, ymd, ip,))
conn.commit()
def platform_info(ip):
sql = "select * FROM users WHERE ip = ?"
c.execute(sql,(ip,))
row = c.fetchone()
if row is None: return ('','',)
return (row['os'],row['os_version'])
def timeout_info(ip):
sql = "select * FROM users WHERE ip = ?"
c.execute(sql,(ip,))
row = c.fetchone()
if row is None: return (0,0,0,)
return [row['current_ts'],row['lasttimestamp'],row['send204after']]
def is_inactive(ip):
ts=tstamp(datetime.datetime.now(tzutc()))
current_ts, last_ts, send204after = timeout_info(ip)
if not last_ts:
return True
if ts - int(last_ts) > INACTIVITY_TO:
return True
else:
return False
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,))
if not send204after:
return False
if ts - int(send204after) > 0:
return True
else:
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()))
sql = 'UPDATE users SET lasttimestamp = ? where ip = ?'
c.execute(sql,(ts,ip,))
conn.commit()
# ################### Action routines based on OS ################3
def microsoft(environ,start_response):
#logger.debug("sending microsoft response")
en_txt={ 'message':"Click on the button to go to the IIAB home page",\
'btn1':"GO TO IIAB HOME 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",\
'btn1':"IIAB",'doc_root':get_iiab_env("WWWROOT")}
if lang == "en":
txt = en_txt
elif lang == "es":
txt = es_txt
response_body = str(j2_env.get_template("simple.template").render(**txt))
status = '200 OK'
response_headers = [('Content-type','text/html'),
('Content-Length',str(len(response_body)))]
start_response(status, response_headers)
return [response_body]
def android(environ, start_response):
global ANDROID_TRIGGERED
ip = environ['HTTP_X_FORWARDED_FOR'].strip()
system,system_version = platform_info(ip)
if system_version[0:1] < '6':
logger.debug("system < 6:%s"%system_version)
location = '/android_splash'
set_204after(ip,0)
else:
set_204after(ip,20)
location = '/android_https'
agent = environ['HTTP_USER_AGENT']
response_body = "hello"
status = '302 Moved Temporarily'
response_headers = [('Location',location)]
start_response(status, response_headers)
return [response_body]
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", \
'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",\
'btn1':"IIAB",'doc_root':get_iiab_env("WWWROOT")}
if lang == "en":
txt = en_txt
elif lang == "es":
txt = es_txt
response_body = str(j2_env.get_template("simple.template").render(**txt))
status = '200 OK'
response_headers = [('Content-type','text/html'),
('Content-Length',str(len(response_body)))]
start_response(status, response_headers)
return [response_body]
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',\
'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",\
'btn1':"IIAB",'doc_root':get_iiab_env("WWWROOT")}
if lang == "en":
txt = en_txt
elif lang == "es":
txt = es_txt
response_body = str(j2_env.get_template("simple.template").render(**txt))
status = '200 OK'
response_headers = [('Content-type','text/html'),
('Content-Length',str(len(response_body)))]
start_response(status, response_headers)
return [response_body]
def mac_splash(environ,start_response):
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',
'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",\
'btn1':"IIAB",'doc_root':get_iiab_env("WWWROOT")}
if lang == "en":
txt = en_txt
elif lang == "es":
txt = es_txt
set_lasttimestamp(ip)
response_body = str(j2_env.get_template("mac.template").render(**txt))
status = '200 Success'
response_headers = [('Content-type','text/html'),
('Content-Length',str(len(response_body)))]
start_response(status, response_headers)
return [response_body]
def macintosh(environ, start_response):
global ip
logger.debug("in function mcintosh")
if not is_inactive(ip):
set_lasttimestamp(ip)
return success(environ,start_response)
# determine if it is time to redirect again
if is_after204_timeout(ip):
set_204after(ip,10)
response_body = """<html><head><script>
window.location.reload(true)
</script></body></html>"""
status = '302 Moved Temporarily'
response_headers = [('content','text/html')]
start_response(status, response_headers)
return [response_body]
else:
return mac_splash(environ,start_response)
def microsoft_connect(environ,start_response):
status = '200 ok'
headers = [('Content-type', 'text/html')]
start_response(status, headers)
return ["Microsoft Connect Test"]
# ============= Return html pages ============================
def banner(environ, start_response):
status = '200 OK'
headers = [('Content-type', 'image/png')]
start_response(status, headers)
image = open("%s/iiab-menu/menu-files/images/iiab_banner6.png"%doc_root, "rb").read()
return [image]
def bootstrap(environ, start_response):
logger.debug("in bootstrap")
status = '200 OK'
headers = [('Content-type', 'text/javascript')]
start_response(status, headers)
boot = open("%s/common/js/bootstrap.min.js"%doc_root, "rb").read()
return [boot]
def jquery(environ, start_response):
logger.debug("in jquery")
status = '200 OK'
headers = [('Content-type', 'text/javascript')]
start_response(status, headers)
boot = open("%s/common/js/jquery.min.js"%doc_root, "rb").read()
return [boot]
def bootstrap_css(environ, start_response):
logger.debug("in bootstrap_css")
status = '200 OK'
headers = [('Content-type', 'text/css')]
start_response(status, headers)
boot = open("%s/common/css/bootstrap.min.css"%doc_root, "rb").read()
return [boot]
def null(environ, start_response):
status = '200 ok'
headers = [('Content-type', 'text/html')]
start_response(status, headers)
return [""]
def success(environ, start_response):
status = '200 ok'
html = '<html><head><title>Success</title></head><body>Success</body></html>'
headers = [('Content-type', 'text/html')]
start_response(status, headers)
return [html]
def put_204(environ, start_response):
status = '204 No Data'
response_body = ''
response_headers = [('Content-type','text/html'),
('Content-Length',str(len(response_body)))]
start_response(status, response_headers)
logger.debug("sending 204 html response")
return [response_body]
def parse_agent(agent):
system = ''
system_version = ''
match = re.search(r"(Android)\s([.\d]*)",agent)
if match:
system = match.group(1)
system_version = match.group(2)
match = re.search(r"(OS X)\s([\d_]*)",agent)
if match:
system = match.group(1)
system_version = match.group(2)
match = re.search(r"(iPhone OS)\s([\d_]*)",agent)
if match:
system = match.group(1)
system_version = match.group(2)
match = re.search(r"(Windows NT)\s([\d.]*)",agent)
if match:
system = match.group(1)
system_version = match.group(2)
return (system, system_version)
#
# ================== Start serving the wsgi application =================
def application (environ, start_response):
global ip
global CATCH
global LIST
global INACTIVITY_TO
global ANDROID_TRIGGERED
# Log the URLs that are not in checkurls
# This "CATCH" mode substitutes this server for apache at port 80
# CATCH mode is started by "iiab-catch" and turned off by "iiab-uncath".
if CATCH:
logger.debug("Checking for url %s. USER_AGENT:%s"%(environ['HTTP_HOST'],\
environ['HTTP_USER_AGENT'],))
if environ['HTTP_HOST'] == '/box.lan':
return
if 'HTTP_X_FORWARDED_FOR' in environ:
ip = environ['HTTP_X_FORWARDED_FOR'].strip()
else:
ip = environ['HTTP_HOST'].strip()
cmd="arp -an %s|gawk \'{print $4}\'" % ip
mac = subprocess.check_output(cmd, shell=True)
data = []
data.append("host: %s\n"%environ['HTTP_HOST'])
data.append("path: %s\n"%environ['PATH_INFO'])
data.append("query: %s\n"%environ['QUERY_STRING'])
data.append("ip: %s\n"%ip)
agent = environ['HTTP_USER_AGENT']
data.append("AGENT: %s\n"%agent)
#print(data)
found = False
url_list = os.path.join(CAPTIVE_PORTAL_BASE,"checkurls")
if os.path.exists(url_list):
with open(url_list,"r") as checkers:
for line in checkers:
if line.find(environ['HTTP_HOST']) > -1:
found = True
break
if not found:
with open(url_list,"a") as checkers:
outstr ="%s\n" % (environ['HTTP_HOST'])
checkers.write(outstr)
data = ['%s: %s\n' % (key, value) for key, value in sorted(environ.items()) ]
logger.debug("This url was missing from checkurls:%s"%data)
# Normal query for captive portal
else:
if 'HTTP_X_FORWARDED_FOR' in environ:
ip = environ['HTTP_X_FORWARDED_FOR'].strip()
else:
data = ['%s: %s\n' % (key, value) for key, value in sorted(environ.items()) ]
#logger.debug("need the correct ip:%s"%data)
ip = environ['REMOTE_ADDR'].strip()
cmd="arp -an %s|gawk \'{print $4}\'" % ip
mac = subprocess.check_output(cmd, shell=True)
data = []
data.append("host: %s\n"%environ['HTTP_HOST'])
data.append("path: %s\n"%environ['PATH_INFO'])
data.append("query: %s\n"%environ['QUERY_STRING'])
data.append("ip: %s\n"%ip)
agent = environ['HTTP_USER_AGENT']
data.append("AGENT: %s\n"%agent)
logger.debug(data)
#print(data)
found = False
return_204_flag = "False"
# record the activity with this ip
ts=tstamp(datetime.datetime.now(tzutc()))
sql = "INSERT or IGNORE INTO users (current_ts,ip) VALUES (?,?)"
c.execute(sql,(ts,ip,))
sql = "UPDATE users SET current_ts = ? where ip = ?"
c.execute(sql,(ts,ip,))
if c.rowcount == 0:
logger.debug("failed UPDATE users SET current_ts = %s WHERE ip = %s"%(ts,ip,))
conn.commit()
ymd=datetime.datetime.today().strftime("%y%m%d-%H%M")
system,system_version = parse_agent(agent)
if system != '':
update_user(ip, mac, system, system_version, ymd)
####### Return pages based upon PATH ###############
# do more specific stuff first
if environ['PATH_INFO'] == "/iiab_banner6.png":
return banner(environ, start_response)
if environ['PATH_INFO'] == "/bootstrap.min.js":
return bootstrap(environ, start_response)
if environ['PATH_INFO'] == "/bootstrap.min.css":
return bootstrap_css(environ, start_response)
if environ['PATH_INFO'] == "/jquery.min.js":
return jquery(environ, start_response)
if environ['PATH_INFO'] == "/favicon.ico":
return null(environ, start_response)
if environ['PATH_INFO'] == "/home_selected":
# the js link to home page triggers this ajax url
# mark the sign-in conversation completed, return 204 or Success or Success
ANDROID_TRIGGERED = True
#data = ['%s: %s\n' % (key, value) for key, value in sorted(environ.items()) ]
#logger.debug("need the correct ip:%s"%data)
logger.debug("function: home_selected. Setting flag to return_204")
#print("setting flag to return_204")
set_204after(ip,PORTAL_TO)
set_lasttimestamp(ip)
status = '200 OK'
headers = [('Content-type', 'text/html')]
start_response(status, headers)
return [""]
#### parse OS platform based upon URL ##################
# mac
if environ['PATH_INFO'] == "/mac_splash":
return mac_splash(environ, start_response)
if environ['PATH_INFO'] == "/step2":
return step2(environ, start_response)
if environ['HTTP_HOST'] == "captive.apple.com" or\
environ['HTTP_HOST'] == "appleiphonecell.com" or\
environ['HTTP_HOST'] == "detectportal.firefox.com" or\
environ['HTTP_HOST'] == "*.apple.com.edgekey.net" or\
environ['HTTP_HOST'] == "gsp1.apple.com" or\
environ['HTTP_HOST'] == "apple.com" or\
environ['HTTP_HOST'] == "www.apple.com":
current_ts, last_ts, send204after = timeout_info(ip)
if not send204after:
# take care of uninitialized state
set_204after(ip,0)
return macintosh(environ, start_response)
# android
if environ['PATH_INFO'] == "/android_splash":
return android_splash(environ, start_response)
if environ['PATH_INFO'] == "/android_https":
return android_https(environ, start_response)
if environ['HTTP_HOST'] == "clients3.google.com" or\
environ['HTTP_HOST'] == "mtalk.google.com" or\
environ['HTTP_HOST'] == "alt7-mtalk.google.com" or\
environ['HTTP_HOST'] == "alt6-mtalk.google.com" or\
environ['HTTP_HOST'] == "connectivitycheck.android.com" or\
environ['HTTP_HOST'] == "connectivitycheck.gstatic.com":
current_ts, last_ts, send204after = timeout_info(ip)
if not last_ts or (ts - int(last_ts) > INACTIVITY_TO):
return android(environ, start_response)
elif is_after204_timeout(ip):
return put_204(environ,start_response)
return #return without doing anything
# microsoft
if environ['PATH_INFO'] == "/connecttest.txt" and not is_inactive(ip):
return microsoft_connect(environ, start_response)
if environ['HTTP_HOST'] == "ipv6.msftncsi.com" or\
environ['HTTP_HOST'] == "ipv6.msftncsi.com.edgesuite.net" or\
environ['HTTP_HOST'] == "www.msftncsi.com" or\
environ['HTTP_HOST'] == "www.msftncsi.com.edgesuite.net" or\
environ['HTTP_HOST'] == "www.msftconnecttest.com" or\
environ['HTTP_HOST'] == "teredo.ipv6.microsoft.com" or\
environ['HTTP_HOST'] == "teredo.ipv6.microsoft.com.nsatc.net":
return microsoft(environ, start_response)
logger.debug("executing the defaut 204 response. [%s"%data)
return put_204(environ,start_response)
# Instantiate the server
httpd = make_server (
"", # The host name
PORT, # A port number where to wait for the request
application # The application object name, in this case a function
)
httpd.serve_forever()
#vim: tabstop=3 expandtab shiftwidth=3 softtabstop=3 background=dark

View file

@ -0,0 +1,21 @@
clients3.google.com
connectivitycheck.gstatic.com
detectportal.firefox.com
*.akamaitechnologies.com
appleiphonecell.com
thinkdifferent.us
*.apple.com.edgekey.net
ipv6.msftncsi.com
ipv6.msftncsi.com.edgesuite.net
www.msftncsi.com
www.msftncsi.com.edgesuite.net
www.msftconnecttest.com
teredo.ipv6.microsoft.com
teredo.ipv6.microsoft.com.nsatc.net
captive.apple.com
init-p01st.push.apple.com
mtalk.google.com
connectivitycheck.android.com
alt7-mtalk.google.com
alt6-mtalk.google.com
captive.lan

View file

@ -0,0 +1,9 @@
#!/bin/bash -x
# substitute our own server to catch OS connectivity checking URL's
systemctl stop apache2
systemctl stop captive-portal
echo address=/#/172.18.96.1 > /etc/dnsmasq.d/capture
/opt/iiab/captive-portal/capture-wsgi.py -d &
# write the pid just started
echo $! > /opt/iiab/captive-portal/pid

View file

@ -0,0 +1,13 @@
#!/bin/bash -x
# Turn off URL recording mode, and return to serving with apache2
kill $(cat /opt/iiab/captive-portal/pid)
# during testing, I start capture by hand -- recorded pid may be stale
pid=$(ps aux | grep "capture-wsgi.py -d" | grep -v grep | awk '{print $2}')
if [ -n "$pid" ];then
kill $pid
fi
awk '{print("address=/" $1 "/172.18.96.1")}' /opt/iiab/captive-portal/checkurls > /etc/dnsmasq.d/capture
awk '{print("ServerAlias ",$1)}' /opt/iiab/captive-portal/checkurls > /etc/apache2/capture
systemctl start captive-portal
systemctl start apache2

View file

@ -62,7 +62,6 @@ transmission_http_port={{ transmission_http_port }}
transmission_peer_port={{ transmission_peer_port }}
sugarizer_port={{ sugarizer_port }}
block_DNS={{ block_DNS }}
captive_portal_enabled={{ captive_portal_enabled }}
py_captive_portal_enabled={{ py_captive_portal_enabled }}
echo "LAN is $lan and WAN is $wan"
@ -124,16 +123,11 @@ if [ "$block_DNS" == "True" ];then
$IPTABLES -t nat -A PREROUTING -i $lan -p udp --dport 53 ! -d {{ lan_ip }} -j DNAT --to {{ lan_ip }}:53
fi
if [ "$captive_portal_enabled" == "True" ];then
$IPTABLES -t mangle -N internet
$IPTABLES -t mangle -A PREROUTING -i {{ iiab_lan_iface }} -p tcp -m tcp --dport 80 -j internet
$IPTABLES -t mangle -A internet -j MARK --set-mark 99
$IPTABLES -t nat -A PREROUTING -i {{ iiab_lan_iface }} -p tcp -m mark --mark 99 -m tcp --dport 80 -j DNAT --to-destination {{ lan_ip }}
elif [ "py_$captive_portal_enabled" == "True" ];then
if [ "$py_captive_portal_enabled" == "True" ];then
$IPTABLES -t nat -A PREROUTING -i $lan -p tcp --dport 80 ! -d {{ lan_ip }} -j DNAT --to {{ lan_ip }}:{{ py_captive_portal_port }}
fi
elif [ "$HTTPCACHE_ON" == "True" ]; then
if [ "$HTTPCACHE_ON" == "True" ]; then
$IPTABLES -t nat -A PREROUTING -i $lan -p tcp --dport 80 ! -d {{ lan_ip }} -j DNAT --to {{ lan_ip }}:3128
fi

View file

@ -158,6 +158,8 @@ wan_nameserver:
# 2-COMMON
<<<<<<< HEAD
<<<<<<< HEAD
# 3-BASE-SERVER
# roles/httpd (Apache configuration) runs here
@ -168,6 +170,34 @@ apache_allow_sudo: True
# For schools that use WordPress and/or Moodle intensively. See iiab/iiab #1147
# WARNING: Enabling this (might) cause excess use of RAM or other resources?
apache_high_php_limits: False
=======
# Intended for developers: ONLY CHANGE THESE IF YOU KNOW WHAT YOU ARE DOING
# The following 2 override the detection when not "auto"
user_wan_iface: auto
user_lan_iface: auto
wan_ip: dhcp
wan_netmask:
wan_gateway:
wan_nameserver:
# exFAT is auto-enabled for all "debuntu" OS's as of Nov 2017, in roles/2-common/tasks/packages.yml#L35-L36
# exFAT_enabled: True
# Parameters by Aggregate Roles
# Each Role must have the following variables which are either True or False:
# <role-name>_install
# <role-name>_enabled
>>>>>>> da77e233... add in template dir
# Our past convention was to install everything in all aggregates
# And to enable everything in 1-PREP, 2-COMMON, and 3-BASE-SERVER
=======
# 3-BASE-SERVER
>>>>>>> ce391f0a... bring in clean defaults and py
# Make this False to disable http://box/common/services/power_off.php button:
allow_apache_sudo: True
# roles/httpd runs here
# roles/iiab-admin runs here