diff --git a/nodes/centaurus.kosmos.org.json b/nodes/centaurus.kosmos.org.json index 539fe71..70f42cc 100644 --- a/nodes/centaurus.kosmos.org.json +++ b/nodes/centaurus.kosmos.org.json @@ -34,6 +34,7 @@ "kosmos_kvm::host", "kosmos-ejabberd::firewall", "kosmos_zerotier::firewall", + "sockethub::_firewall", "apt::default", "timezone_iii::default", "timezone_iii::debian", @@ -85,6 +86,7 @@ "recipe[kosmos_assets::nginx_site]", "recipe[kosmos_kvm::host]", "recipe[kosmos-ejabberd::firewall]", - "recipe[kosmos_zerotier::firewall]" + "recipe[kosmos_zerotier::firewall]", + "recipe[sockethub::_firewall]" ] } \ No newline at end of file diff --git a/nodes/nodejs-2.json b/nodes/nodejs-2.json index 17fcf80..f470672 100644 --- a/nodes/nodejs-2.json +++ b/nodes/nodejs-2.json @@ -12,7 +12,8 @@ "hostname": "nodejs-2", "ipaddress": "192.168.122.243", "roles": [ - "kredits_github" + "kredits_github", + "sockethub" ], "recipes": [ "kosmos-base", @@ -21,6 +22,9 @@ "kredits-github", "kredits-github::default", "kredits-github::nginx", + "sockethub", + "sockethub::default", + "sockethub::proxy", "apt::default", "timezone_iii::default", "timezone_iii::debian", @@ -51,6 +55,14 @@ "nginx::commons_script", "nginx::commons_conf", "kosmos-nginx::firewall", + "kosmos-redis::default", + "redis::server", + "redis::default", + "backup::default", + "logrotate::default", + "nodejs::npm", + "nodejs::install", + "sockethub::_firewall", "kosmos-base::letsencrypt" ], "platform": "ubuntu", @@ -70,6 +82,7 @@ "run_list": [ "recipe[kosmos-base]", "recipe[kosmos-hubot::wormhole]", - "role[kredits_github]" + "role[kredits_github]", + "role[sockethub]" ] } \ No newline at end of file diff --git a/roles/sockethub.rb b/roles/sockethub.rb new file mode 100644 index 0000000..277bd23 --- /dev/null +++ b/roles/sockethub.rb @@ -0,0 +1,6 @@ +name "sockethub" + +run_list %w( + sockethub::default + sockethub::proxy +) diff --git a/site-cookbooks/sockethub/attributes/default.rb b/site-cookbooks/sockethub/attributes/default.rb index 7935b9b..c852d3b 100644 --- a/site-cookbooks/sockethub/attributes/default.rb +++ b/site-cookbooks/sockethub/attributes/default.rb @@ -1,4 +1,5 @@ node.default['sockethub']['port'] = '10551' node.default['sockethub']['external_port'] = '10550' -node.default['sockethub']['revision'] = 'v3.0.1' +node.default['sockethub']['version'] = '4.0.1' node.default['sockethub']['nginx']['server_name'] = 'sockethub.kosmos.org' +node.default['sockethub']['debug_logs'] = 'sockethub*' diff --git a/site-cookbooks/sockethub/metadata.rb b/site-cookbooks/sockethub/metadata.rb index 0c5347b..db2d286 100644 --- a/site-cookbooks/sockethub/metadata.rb +++ b/site-cookbooks/sockethub/metadata.rb @@ -4,9 +4,8 @@ maintainer_email 'mail@kosmos.org' license 'MIT' description 'Installs/Configures sockethub' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version '0.1.1' +version '0.2.0' -depends 'application_javascript' depends 'kosmos-redis' depends 'kosmos-nodejs' depends 'kosmos-nginx' diff --git a/site-cookbooks/sockethub/recipes/default.rb b/site-cookbooks/sockethub/recipes/default.rb index df30690..59f6412 100644 --- a/site-cookbooks/sockethub/recipes/default.rb +++ b/site-cookbooks/sockethub/recipes/default.rb @@ -27,11 +27,15 @@ include_recipe 'kosmos-nodejs' include_recipe 'kosmos-redis' -group "sockethub" do +user = "sockethub" +group = "sockethub" +entry = "/usr/bin/sockethub" + +group group do gid 7625 end -user "sockethub" do +user user do comment "sockethub user" uid 7625 gid 7625 @@ -39,47 +43,43 @@ user "sockethub" do shell "/bin/bash" end -path_to_deploy = "/opt/sockethub" -application path_to_deploy do - owner "sockethub" - group "sockethub" - - git do - user "sockethub" - group "sockethub" - repository 'https://github.com/sockethub/sockethub.git' - revision node['sockethub']['revision'] - end - - npm_install do - user "sockethub" - end - - execute "systemctl daemon-reload" do - command "systemctl daemon-reload" - action :nothing - end - - template "/lib/systemd/system/sockethub_nodejs.service" do - source 'nodejs.systemd.service.erb' - owner 'root' - group 'root' - mode '0644' - variables( - user: "sockethub", - group: "sockethub", - app_dir: path_to_deploy, - entry: "/usr/bin/node /usr/bin/npm start", - environment: { 'DEBUG' => '*', - 'PORT' => node['sockethub']['port'], - # Use the second database (index starts at 0) - 'REDIS_URL' => "redis://localhost:6379/1" } - ) - notifies :run, "execute[systemctl daemon-reload]", :delayed - notifies :restart, "service[sockethub_nodejs]", :delayed - end - - service "sockethub_nodejs" do - action [:enable, :start] - end +npm_package "sockethub" do + version node['sockethub']['version'] +end + +execute "systemctl daemon-reload" do + command "systemctl daemon-reload" + action :nothing +end + +environment_variables = { + 'PORT' => node['sockethub']['port'], + # Use the second database (index starts at 0) + 'REDIS_URL' => "redis://localhost:6379/1" +} +unless node['sockethub']['debug_logs'].nil? + environment_variables['DEBUG'] = node['sockethub']['debug_logs'] +end + +environment = environment_variables.map{|k, v| "'#{k}=#{v}'"}.join(' ') + +systemd_unit "sockethub_nodejs.service" do + content <<-EOF +[Unit] +Description=Start sockethub +Requires=redis-server.service +After=redis-server.service + +[Service] +ExecStart=#{entry} +User=#{user} +Group=#{group} +Environment=#{environment} +Restart=always + +[Install] +WantedBy=multi-user.target + EOF + triggers_reload true + action [:create, :enable, :start] end diff --git a/site-cookbooks/sockethub/recipes/proxy.rb b/site-cookbooks/sockethub/recipes/proxy.rb index b707693..29c753c 100644 --- a/site-cookbooks/sockethub/recipes/proxy.rb +++ b/site-cookbooks/sockethub/recipes/proxy.rb @@ -26,8 +26,41 @@ include_recipe 'sockethub::_firewall' include_recipe 'kosmos-nginx' +include_recipe "kosmos-base::letsencrypt" + server_name = node['sockethub']['nginx']['server_name'] +nginx_post_hook = <<-EOF +#!/usr/bin/env bash + +set -e + +systemctl reload nginx +EOF + +file "/etc/letsencrypt/renewal-hooks/post/nginx" do + content nginx_post_hook + mode 0755 + owner "root" + group "root" +end + +gandi_api_data_bag_item = data_bag_item('credentials', 'gandi_api_5apps') + +template "/root/gandi_dns_certbot_hook.sh" do + variables gandi_api_key: gandi_api_data_bag_item["key"] + mode 0770 +end + +# Generate a Let's Encrypt cert (only if no cert has been generated before). +# The systemd timer will take care of renewing +execute "letsencrypt cert for sockethub" do + command "certbot certonly --manual --preferred-challenges dns --manual-public-ip-logging-ok --agree-tos --manual-auth-hook \"/root/gandi_dns_certbot_hook.sh auth\" --manual-cleanup-hook \"/root/gandi_dns_certbot_hook.sh cleanup\" --deploy-hook \"/etc/letsencrypt/renewal-hooks/post/nginx\" --email ops@kosmos.org -d #{server_name} -n" + not_if do + File.exist?("/etc/letsencrypt/live/#{server_name}/fullchain.pem") + end +end + template "#{node['nginx']['dir']}/sites-available/#{server_name}" do source 'nginx_conf_sockethub.erb' owner 'www-data' @@ -40,13 +73,7 @@ template "#{node['nginx']['dir']}/sites-available/#{server_name}" do notifies :reload, 'service[nginx]', :delayed end -# Legacy vhost -nginx_site "sockethub" do - action :disable -end - nginx_site server_name do action :enable end -nginx_certbot_site server_name diff --git a/site-cookbooks/sockethub/templates/default/nginx_conf_sockethub.erb b/site-cookbooks/sockethub/templates/default/nginx_conf_sockethub.erb index 4ed2689..a25bd84 100644 --- a/site-cookbooks/sockethub/templates/default/nginx_conf_sockethub.erb +++ b/site-cookbooks/sockethub/templates/default/nginx_conf_sockethub.erb @@ -8,10 +8,13 @@ map $http_upgrade $connection_upgrade { '' close; } -<% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%> server { + <% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%> listen <%= @sockethub_external_port %> ssl http2; add_header Strict-Transport-Security "max-age=15768000"; + <% else -%> + listen <%= @sockethub_external_port %>; + <% end -%> server_name <%= @server_name %>; @@ -32,7 +35,8 @@ server { proxy_set_header Connection $connection_upgrade; } + <% if File.exist?(@ssl_cert) && File.exist?(@ssl_key) -%> ssl_certificate <%= @ssl_cert %>; ssl_certificate_key <%= @ssl_key %>; + <% end -%> } -<% end -%> diff --git a/site-cookbooks/sockethub/templates/default/nodejs.systemd.service.erb b/site-cookbooks/sockethub/templates/default/nodejs.systemd.service.erb deleted file mode 100644 index 8dc98a9..0000000 --- a/site-cookbooks/sockethub/templates/default/nodejs.systemd.service.erb +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Start nodejs app -Requires=redis-server.service -After=redis-server.service - -[Service] -ExecStart=<%= @entry %> -WorkingDirectory=<%= @app_dir %> -User=<%= @user %> -Group=<%= @group %> -<% unless @environment.empty? -%> -Environment=<% @environment.each do |key, value| -%>'<%= key %>=<%= value %>' <% end %> -<% end -%> -Restart=always - -[Install] -WantedBy=multi-user.target diff --git a/site-cookbooks/sockethub/templates/gandi_dns_certbot_hook.sh.erb b/site-cookbooks/sockethub/templates/gandi_dns_certbot_hook.sh.erb new file mode 100755 index 0000000..d0ed9dc --- /dev/null +++ b/site-cookbooks/sockethub/templates/gandi_dns_certbot_hook.sh.erb @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# + +set -euf -o pipefail + +# ************** USAGE ************** +# +# Example usage (with this hook file saved in /root/): +# +# sudo su - +# certbot certonly --manual --preferred-challenges dns --manual-public-ip-logging-ok --agree-tos -d "5apps.com" -d muc.5apps.com -d "xmpp.5apps.com" \ +# --manual-auth-hook "/root/letsencrypt_hook.sh auth" --manual-cleanup-hook "/root/letsencrypt_hook.sh cleanup" +# +# This hook requires configuration, continue reading. +# +# ************** CONFIGURATION ************** +# +# GANDI_API_KEY: Your Gandi Live API key +# +# PROVIDER_UPDATE_DELAY: +# How many seconds to wait after updating your DNS records. This may be required, +# depending on how slow your DNS host is to begin serving new DNS records after updating +# them via the API. 30 seconds is a safe default, but some providers can be very slow +# (e.g. Linode). +# +# Defaults to 30 seconds. +# +GANDI_API_KEY="<%= @gandi_api_key %>" +PROVIDER_UPDATE_DELAY=30 + +regex='.*\.(.*\..*)' +if [[ $CERTBOT_DOMAIN =~ $regex ]] +then + DOMAIN="${BASH_REMATCH[1]}" +else + DOMAIN="${CERTBOT_DOMAIN}" +fi + +# To be invoked via Certbot's --manual-auth-hook +function auth { + curl -s -D- -H "Content-Type: application/json" \ + -H "X-Api-Key: ${GANDI_API_KEY}" \ + -d "{\"rrset_name\": \"_acme-challenge.${CERTBOT_DOMAIN}.\", + \"rrset_type\": \"TXT\", + \"rrset_ttl\": 3600, + \"rrset_values\": [\"${CERTBOT_VALIDATION}\"]}" \ + "https://dns.api.gandi.net/api/v5/domains/${DOMAIN}/records" + + + sleep ${PROVIDER_UPDATE_DELAY} +} + +# To be invoked via Certbot's --manual-cleanup-hook +function cleanup { + curl -s -X DELETE -H "Content-Type: application/json" \ + -H "X-Api-Key: ${GANDI_API_KEY}" \ + https://dns.api.gandi.net/api/v5/domains/${DOMAIN}/records/_acme-challenge.${CERTBOT_DOMAIN}./TXT +} + +HANDLER=$1; shift; +if [ -n "$(type -t $HANDLER)" ] && [ "$(type -t $HANDLER)" = function ]; then + $HANDLER "$@" +fi