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:
parent
49a0523074
commit
4ee9c3e2b5
16 changed files with 897 additions and 38 deletions
BIN
roles/1-prep/templates/.iiab.env.j2.un~
Normal file
BIN
roles/1-prep/templates/.iiab.env.j2.un~
Normal file
Binary file not shown.
|
@ -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 }}
|
||||
|
|
|
@ -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>
|
||||
|
||||
<Directory "{{ doc_root }}">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</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.
|
||||
|
|
54
roles/network/files/mac.template
Normal file
54
roles/network/files/mac.template
Normal 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>
|
100
roles/network/files/simple.template
Normal file
100
roles/network/files/simple.template
Normal 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>
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
14
roles/network/templates/captive-portal/captive-portal.conf
Normal file
14
roles/network/templates/captive-portal/captive-portal.conf
Normal 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>
|
|
@ -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
|
||||
|
550
roles/network/templates/captive-portal/capture-wsgi.py
Executable file
550
roles/network/templates/captive-portal/capture-wsgi.py
Executable 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
|
||||
|
21
roles/network/templates/captive-portal/checkurls
Executable file
21
roles/network/templates/captive-portal/checkurls
Executable 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
|
9
roles/network/templates/captive-portal/iiab-catch
Executable file
9
roles/network/templates/captive-portal/iiab-catch
Executable 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
|
13
roles/network/templates/captive-portal/iiab-uncatch
Executable file
13
roles/network/templates/captive-portal/iiab-uncatch
Executable 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
|
|
@ -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 }}
|
||||
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 [ "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 }}
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue