diff --git a/doc/PXEN310-0000_TN_PXPortal_Integration.md b/doc/PXEN310-0000_TN_PXPortal_Integration.md new file mode 100644 index 0000000..d334864 --- /dev/null +++ b/doc/PXEN310-0000_TN_PXPortal_Integration.md @@ -0,0 +1,419 @@ + + +# Introduction + +A captive portal is a web page accessed with a web browser that is displayed to newly connected users of a Wi-Fi network before they are granted broader access to network resources. + +To determinate if a captive portal has to be display and ask to passenger an action, a phone (Android or iOS) calls some APIs. When this API sends: + +* 204 HTTP Code: device has connectivity nothing to do. +* 302 HTTP Code: device maybe have connectivity but passenger has to signin. +* _No answer_: device don't have connectivity but device doesn't known what to do + +The goal of PXPortal is to simulate captive portal APIs from different constructors by answering required HTTP codes. + +__WARNING__ + +The behavior describes here is experimental. Such as every phone constructor implements its own API or internal mechanism, this solution cannot work on all phones (for example some Samsung, ...). The list of captive API will be updated over time. + +# PXPortal + +PXPortal is composed of three services: + +* nginx: a web server which can also be used as a reverse proxy, load balancer, mail proxy and HTTP cache +* dnsmasq: provides Domain Name System (DNS) forwarder, Dynamic Host Configuration Protocol (DHCP) server, router advertisement and network boot features for small computer networks, created as free software +* portal_service: internal PXCom service to simuate captive portal API + + +```{#plantuml title="Device without internet but pxportal enabled"} +participant Device +participant Box +participant Dnsmasq +participant Nginx +participant Portal_Service + +Device -> Box: connectivitycheck.google.com +Box -> Dnsmasq: Whois google.com +Dnsmasq --> Box: server +Box -> Nginx: connectivitycheck.google.com +Nginx -> Portal_Service: Is a validated mac address +alt Not Valid + Portal_Service --> Nginx: Code 302 - Redirect to http://[PortalPage] +else + Portal_Service --> Nginx: Code 204 +end +Nginx --> Box: Portal_Service response +Box --> Device: Portal_Service response +``` + +To have a validated mac address, a passenger has to accept terms of use on portal webpage. On this action, a request is sent to portal_service which will save in memory the mac address as a valid one. Next time, passenger will be considered as a valid user and no new signin notification will be shown. + +```{#plantuml title="Device without internet but pxportal enabled"} +participant Device +participant Portal_Service + +Device -> Portal_Service: http://captive.eca.aero/validate +Portal_Service -> Portal_Service: Flag MAC as valid +Portal_Service --> Device: OK +``` + +\clearpage + + +## PXPortal - Dnsmasq + +* redirects all captive portal API to the box. With the following configuration, PXPortal can handles Android and iOS phones and some linux. +* _(optional)_ can provide a DHCP server (not used here) + +```bash +# ANDROID +address=/google.com/10.0.0.254 +address=/.google.com/10.0.0.254 +address=/gstatic.com/10.0.0.254 +address=/.gstatic.com/10.0.0.254 +address=/android.com/10.0.0.254 +address=/.android.com/10.0.0.254 +# IOS +address=/apple.com/10.0.0.254 +address=/.apple.com/10.0.0.254 +# BROWSER +address=/firefox.com/10.0.0.254 +address=/.firefox.com/10.0.0.254 +# LINUX +address=/ubuntu.com/10.0.0.254 +address=/.ubuntu.com/10.0.0.254 +address=/gnome.org/10.0.0.254 +address=/.gnome.org/10.0.0.254 +``` + +## PXPortal - Nginx + +* handles all requests sent to captive portal API and forwards them to our pxportal service, +* and serves portal webpages on _http://portal.eca.aero/index.html_ and _http://portal.eca.aero/ready.html_ + + +```bash +server { + server_name captive.eca.aero + *.apple.com + *.gstatic.com + *.firefox.com + *.ubuntu.com + *.google.com + *.android.com + *.gnome.org; + + location / { + proxy_pass http://pxportal_service:8889/; + proxy_set_header Host $host; + proxy_set_header X-Real-Ip $remote_addr; + proxy_buffering off; + } +} + +server { + server_name portal.eca.aero; + root /data/webapp/portal; +} +``` + +## PXPortal - Portal_Service + +* is a server HTTP based on NodeJs and ExpressJs +* simulates captive portal APIs +* saves validated mac addresses on _/validate_ call. It's saved in memory that's mean, it's cleared on one service restart +* performs an ARP command to find mac address from IP client + + + + +\clearpage + +# Deployment + +PXPortal can be deployed with a docker-compose. + +Before continuing, be sure [docker](https://docs.docker.com/install/linux/docker-ce/ubuntu/) and [docker-compose](https://docs.docker.com/compose/install/) are installed on your machine. + + +## First Run + +### 1. Clone ECA-captive repository + +```bash +git clone ssh://gitolite@git.pxcom.aero:2221/ife/ECA-captive.git +``` + +This repository contains: + +* __docker-compose.yml__: docker config of three PXPortal services +* __conf__: dnsmasq and Nginx configuration +* __webapp__: sources of portal webpage + + +```{#plantuml title="ECA-captive tree"} +@startsalt +{ +{T ++ECA-captive/ +++ conf ++++ conf.d +++++ portal.conf ++++ dnsmasq.conf ++++ nginx.conf +++ docker-compose.yml +++ webapp +} +} +@endsalt +``` + +### 2. Run PXPortal + +```bash +# Go into ECA-captive folder +cd ECA-captive +# Start all services in daemon mode +docker-compose up -d +``` + + +### 3. Update PXPortal + +_(if updates are available)_ + +```bash +# Go into ECA-captive folder +cd ECA-captive +# Pull latest updated dockers +docker-compose pull +# Restart dockers +docker-compose up -d +``` + +\clearpage + +## SSH Config + +Such as portal_service requires MAC addresses thanks to ARP command. But from one docker context, the command has to execute from the host in order to get MAC addresses from IP clients. This is _how to do this_ in few steps + +### 1. Generate a docker SSH Public key + +```bash +### On your HOST +# Launch portal_service shell +docker exec -it pxportal_service sh + +### On portal_service shell +# Generate a SSH key to be able to send ARP command from docker to host +ssh-keygen +# Display and copy ssh public key +cat /root/.ssh/id_rsa.pub + +### Exit portal_shell +# Edit HOST ssh authorized_keys and paste previous ssh public key of portal_service +vi ~/.ssh/authorized_keys +``` + +### 2. Update HOST IP address from portal_service + +```bash +### On your HOST +# Launch portal_service shell +docker exec -it pxportal_service sh + +### On portal_service shell +# Show all network interfaces available +ifconfig +# Keep in memory the inet addr of eth0 +# HOST IP address from docker should be this IP address by remplacing last number by 1 +# EX: 172.20.0.2 => 172.20.0.1 = HOST_IP +# Check your ssh public key is set correctly and update known_hosts on first SSH connection +ssh [HOST_NAME]@[HOST_IP] +# EX ssh elta@172.20.0.1 +# Check ARP command can be run +ssh [HOST_NAME]@[HOST_IP] arp -n +# A list of IP with matching MAC will be appeared +# If not, try again from the beginning +``` + +### 3. Update docker-compose.yml ARP_CMD + +Update the environment variable __ARP_CMD__ in _your docker-compose.yml_ with the right HOST_NAME and HOST_IP found in step 2. + + +```bash +# Go into ECA-captive folder +cd ECA-captive +# Start all services in daemon mode +docker-compose up -d +``` + +\clearpage + +## Primary Nginx Config + +The ECA-portal Nginx is configured as a slave reverse proxy. That's why it is not used the 80 by default. + +But to be able to handle captive portal API, the primary nginx running on port 80 should redirect all unknown URLS to portal nginx. This is an example of configuration to add into a primary nginx configuration: + + +```bash +.... +server { + listen 80 default_server; + server_name _; + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Host $remote_addr; + proxy_pass http://172.19.0.1:8031; + } + + access_log captive-pxportal-access.log; + error_log captive-pxportal-error.log; +} +.... +``` + + +\clearpage + +## docker-compose.yml + +```yaml +version: '2' + +services: + nginx: + image: nginx:alpine + container_name: pxportal_nginx + networks: + - pxportal + ports: + - "8031:80" + volumes: + - ./webapp:/data/webapp + - ./conf/nginx.conf:/etc/nginx/nginx.conf + - ./conf/conf.d:/etc/nginx/conf.d/ + + dnsmasq: + image: andyshinn/dnsmasq + container_name: pxportal_dnsmasq + ports: + - "53:53/tcp" + - "53:53/udp" + cap_add: + - NET_ADMIN + volumes: + - ./conf/dnsmasq.conf:/etc/dnsmasq.conf + depends_on: + - pxportal + + pxportal: + container_name: pxportal_service + image: registry.preprod.pxcom.aero/pxcom-servers/pxportal-srv:latest + volumes: + - ./webapp:/usr/app/webapp + - ssh:/root/.ssh + networks: + - pxportal + environment: + - REDIRECT_TO=http://portal.eca.aero/index.html +# TO UPDATE FROM SSH Config part + - ARP_CMD=ssh elta@172.21.0.1 arp -n + ports: + - "8889:8889" + +volumes: + ssh: + +networks: + pxportal: + driver: bridge +``` + +\clearpage + +## Cisco configuration + +### Wlan + +#### Wlan - Home + +Go to __Wireless Settings / WLANs__ page to create or configure a WLAN for passengers. + +![Wlan Home](assets/wlan_home.png){ width=100% } +\FloatBarrier + +#### Wlan - General + +* __Profile Name__: [Name of passengers WIFI] +* __SSID__: [Name of passengers WIFI] + +![Wlan Edition - General](assets/wlan_edit_general.png){ width=100% } + +\clearpage + +#### Wlan - WLAN Security + +Such as CISCO captive portal forces a HTTPS page and requires to check SSL certificates from external network, PXPortal doesn't use it and provides its own captive portal. That's why __Guest Network__ is disabled and the WIFI network is opened. + +* __Guest Network__: _disabled_ +* __Security type__: _Open_ + +![Wlan Edition - WLAN Security](assets/wlan_edit_security.png){ width=100% } +\FloatBarrier + + +#### Wlan - VLAN & Firwall + +_Default configuration_ + +![Wlan Edition - VLAN & Firewall](assets/wlan_edit_vlan_firewall.png){ width=100% } +\FloatBarrier + +\clearpage + +#### Wlan - Traffic Shaping + +_Default configuration_ + +![Wlan Edition - Traffic Shaping](assets/wlan_edit_traffic.png){ width=100% } +\FloatBarrier + +\clearpage + +### DHCP + +By default, PXPortal uses Cisco DHCP. + +### General + +* __Pool Status__: Enabled +* __Network / Mask__: 10.0.0.0 / 255.255.255.0 +* __Start IP__: 10.0.0.11 +* __End IP__: 10.0.0.249 +* __Gateway IP__: 10.0.0.254 +* __Domain Name__: eca.aero +* __Name servers__: User Defined / 10.0.0.254 + +![DHCP Edition](assets/dhcp_edit.png){ width=100% } +\FloatBarrier diff --git a/doc/PXEN310-0000_TN_PXPortal_Integration.pdf b/doc/PXEN310-0000_TN_PXPortal_Integration.pdf new file mode 100644 index 0000000..05b94f9 Binary files /dev/null and b/doc/PXEN310-0000_TN_PXPortal_Integration.pdf differ diff --git a/doc/assets/dhcp_edit.png b/doc/assets/dhcp_edit.png new file mode 100755 index 0000000..4bfaa87 Binary files /dev/null and b/doc/assets/dhcp_edit.png differ diff --git a/doc/assets/wlan_edit_general.png b/doc/assets/wlan_edit_general.png new file mode 100755 index 0000000..6c63ee7 Binary files /dev/null and b/doc/assets/wlan_edit_general.png differ diff --git a/doc/assets/wlan_edit_security.png b/doc/assets/wlan_edit_security.png new file mode 100755 index 0000000..44adf0b Binary files /dev/null and b/doc/assets/wlan_edit_security.png differ diff --git a/doc/assets/wlan_edit_traffic.png b/doc/assets/wlan_edit_traffic.png new file mode 100755 index 0000000..7085505 Binary files /dev/null and b/doc/assets/wlan_edit_traffic.png differ diff --git a/doc/assets/wlan_edit_vlan_firewall.png b/doc/assets/wlan_edit_vlan_firewall.png new file mode 100755 index 0000000..2a2a8b0 Binary files /dev/null and b/doc/assets/wlan_edit_vlan_firewall.png differ diff --git a/doc/assets/wlan_home.png b/doc/assets/wlan_home.png new file mode 100755 index 0000000..16a2e26 Binary files /dev/null and b/doc/assets/wlan_home.png differ