Skip to content

Commit

Permalink
✨ Adds Hassio Ingress support
Browse files Browse the repository at this point in the history
  • Loading branch information
frenck committed Apr 12, 2019
1 parent 787603e commit 4f3d08b
Show file tree
Hide file tree
Showing 20 changed files with 391 additions and 72 deletions.
7 changes: 6 additions & 1 deletion node-red/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ RUN \
\
&& apk add --no-cache \
git=2.20.1-r0 \
lua-resty-http=0.12-r1 \
nginx-mod-http-lua=1.14.2-r0 \
nginx=1.14.2-r0 \
nodejs=10.14.2-r0 \
npm=10.14.2-r0 \
openssh-client=7.9_p1-r4 \
Expand Down Expand Up @@ -59,7 +62,9 @@ RUN \
&& npm cache clear --force \
\
&& apk del --no-cache --purge .build-dependencies \
&& rm -fr /tmp/*
&& rm -fr \
/tmp/* \
/etc/nginx

# Copy root filesystem
COPY rootfs /
Expand Down
13 changes: 10 additions & 3 deletions node-red/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,24 @@
"slug": "nodered",
"description": "Flow-based programming for the Internet of Things",
"url": "https://github.com/hassio-addons/addon-node-red",
"webui": "[PROTO:ssl]://[HOST]:[PORT:1880]",
"webui": "[PROTO:ssl]://[HOST]:[PORT:80]",
"ingress": true,
"ingress_port": 0,
"startup": "application",
"homeassistant": "0.91.3",
"arch": [
"aarch64",
"amd64",
"armhf",
"armv7",
"i386"
],
"ports": {
"80/tcp": null
},
"ports_description": {
"80/tcp": "Web interface (Not required for Hass.io Ingress)"
},
"boot": "auto",
"hassio_api": true,
"hassio_role": "manager",
Expand Down Expand Up @@ -44,7 +53,6 @@
"username": "",
"password": ""
},
"port": 1880,
"ssl": true,
"certfile": "fullchain.pem",
"keyfile": "privkey.pem",
Expand All @@ -65,7 +73,6 @@
"username": "str",
"password": "str"
},
"port": "port",
"ssl": "bool",
"certfile": "str",
"keyfile": "str",
Expand Down
34 changes: 34 additions & 0 deletions node-red/rootfs/etc/cont-init.d/11-nginx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Community Hass.io Add-ons: Node-RED
# Configures NGINX for use with Node-RED
# ==============================================================================
declare admin_port
declare certfile
declare ingress_interface
declare ingress_port
declare keyfile

admin_port=$(bashio::addon.port 80)
if bashio::var.has_value "${admin_port}"; then
bashio::config.require.ssl

if bashio::config.true 'ssl'; then
certfile=$(bashio::config 'certfile')
keyfile=$(bashio::config 'keyfile')

mv /etc/nginx/servers/direct-ssl.disabled /etc/nginx/servers/direct.conf
sed -i "s/%%certfile%%/${certfile}/g" /etc/nginx/servers/direct.conf
sed -i "s/%%keyfile%%/${keyfile}/g" /etc/nginx/servers/direct.conf

else
mv /etc/nginx/servers/direct.disabled /etc/nginx/servers/direct.conf
fi

sed -i "s/%%port%%/${admin_port}/g" /etc/nginx/servers/direct.conf
fi

ingress_port=$(bashio::addon.ingress_port)
ingress_interface=$(bashio::addon.ip_address)
sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf
sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf
96 changes: 96 additions & 0 deletions node-red/rootfs/etc/nginx/includes/mime.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;

text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;

image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;

font/woff woff;
font/woff2 woff2;

application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;

application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;

audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;

video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}
15 changes: 15 additions & 0 deletions node-red/rootfs/etc/nginx/includes/proxy_params.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
proxy_http_version 1.1;
proxy_ignore_client_abort off;
proxy_read_timeout 86400s;
proxy_redirect off;
proxy_send_timeout 86400s;
proxy_max_temp_file_size 0;

proxy_set_header Accept-Encoding "";
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
1 change: 1 addition & 0 deletions node-red/rootfs/etc/nginx/includes/resolver.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
resolver 172.30.32.2;
6 changes: 6 additions & 0 deletions node-red/rootfs/etc/nginx/includes/server_params.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
root /dev/null;
server_name $hostname;

add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;
9 changes: 9 additions & 0 deletions node-red/rootfs/etc/nginx/includes/ssl_params.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
3 changes: 3 additions & 0 deletions node-red/rootfs/etc/nginx/includes/upstream.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
upstream backend {
server 127.0.0.1:46836;
}
83 changes: 83 additions & 0 deletions node-red/rootfs/etc/nginx/lua/ha-auth.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
local http = require "resty.http"
local auths = ngx.shared.auths

function authenticate()

--- Test Authentication header is set and with a value
local header = ngx.req.get_headers()['Authorization']
if header == nil or header:find(" ") == nil then
return false
end

local divider = header:find(' ')
if header:sub(0, divider-1) ~= 'Basic' then
return false
end

local auth = ngx.decode_base64(header:sub(divider+1))
if auth == nil or auth:find(':') == nil then
return false
end

divider = auth:find(':')
local username = auth:sub(0, divider-1)
local password = auth:sub(divider+1)

--- Check if authentication is cached
if auths:get(username) == password then
ngx.log(ngx.DEBUG, "Authenticated user against Home Assistant (cache).")
return true
end

--- HTTP request against Hassio API
local httpc = http.new()
local res, err = httpc:request_uri("http://hassio/auth", {
method = "POST",
body = ngx.encode_args({["username"]=username, ["password"]=password}),
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
["X-HASSIO-KEY"] = os.getenv("HASSIO_TOKEN"),
},
keepalive_timeout = 60,
keepalive_pool = 10
})

--- Error during API request
if err then
ngx.log(ngx.WARN, "Error during Hassio user authentication.", err)
return false
end

--- No result? Something went wrong...
if not res then
ngx.log(ngx.WARN, "Error during Hassio user authentication.")
return false
end

--- Valid response, the username/password is valid
if res.status == 200 then
ngx.log(ngx.INFO, "Authenticated user against Home Assistant.")
auths:set(username, password, 60)
return true
end

--- Whatever the response is, it is invalid
ngx.log(ngx.WARN, "Authentication against Home Assistant failed!")
return false
end

-- Only authenticate if its not disabled
if not os.getenv('DISABLE_HA_AUTHENTICATION') then

--- Try to authenticate against HA
local authenticated = authenticate()

--- If authentication failed, throw a basic auth
if not authenticated then
ngx.header.content_type = 'text/plain'
ngx.header.www_authenticate = 'Basic realm="Home Assistant"'
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say('401 Access Denied')
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
end
1 change: 1 addition & 0 deletions node-red/rootfs/etc/nginx/modules/ndk_http.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
load_module "/usr/lib/nginx/modules/ndk_http_module.so";
1 change: 1 addition & 0 deletions node-red/rootfs/etc/nginx/modules/ngx_http_lua.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
load_module "/usr/lib/nginx/modules/ngx_http_lua_module.so";
58 changes: 58 additions & 0 deletions node-red/rootfs/etc/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Run nginx in foreground.
daemon off;

# This is run inside Docker.
user root;

# Pid storage location.
pid /var/run/nginx.pid;

# Set number of worker processes.
worker_processes 1;

# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;

# Write error log to Hass.io add-on log.
error_log /proc/1/fd/1 error;

# Load allowed environment vars
env HASSIO_TOKEN;
env DISABLE_HA_AUTHENTICATION;

# Load dynamic modules.
include /etc/nginx/modules/*.conf;

# Max num of simultaneous connections by a worker process.
events {
worker_connections 512;
}

http {
include /etc/nginx/includes/mime.types;

log_format hassio '[$time_local] $status '
'$http_x_forwarded_for($remote_addr) '
'$request ($http_user_agent)';

access_log /proc/1/fd/1 hassio;
client_max_body_size 4G;
default_type application/octet-stream;
gzip on;
keepalive_timeout 65;
lua_shared_dict auths 16k;
sendfile on;
server_tokens off;
tcp_nodelay on;
tcp_nopush on;

map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

include /etc/nginx/includes/resolver.conf;
include /etc/nginx/includes/upstream.conf;

include /etc/nginx/servers/*.conf;
}
16 changes: 16 additions & 0 deletions node-red/rootfs/etc/nginx/servers/direct-ssl.disabled
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
server {
listen %%port%% default_server ssl http2;

include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/ssl_params.conf;
include /etc/nginx/includes/proxy_params.conf;

ssl on;
ssl_certificate /ssl/%%certfile%%;
ssl_certificate_key /ssl/%%keyfile%%;

location / {
access_by_lua_file /etc/nginx/lua/ha-auth.lua;
proxy_pass http://backend;
}
}
Loading

2 comments on commit 4f3d08b

@deece
Copy link

@deece deece commented on 4f3d08b Apr 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit breaks the addon on ARM64. See: #199

The LUA module requires a patch (already in mainline), so it will have to be built from source until the Alpine packages catch up: openresty/lua-nginx-module#1379

@frenck
Copy link
Member Author

@frenck frenck commented on 4f3d08b Apr 18, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not comment on commits, use issue tracker. Thanks.

Please sign in to comment.