Add framework for managing letsencrypt certificates
authorTom Hughes <tom@compton.nu>
Sat, 11 Feb 2017 15:03:01 +0000 (15:03 +0000)
committerTom Hughes <tom@compton.nu>
Sat, 11 Feb 2017 16:35:17 +0000 (16:35 +0000)
19 files changed:
.kitchen.yml
cookbooks/letsencrypt/.foodcritic [new file with mode: 0644]
cookbooks/letsencrypt/README.md [new file with mode: 0644]
cookbooks/letsencrypt/files/default/bin/renew [new file with mode: 0755]
cookbooks/letsencrypt/files/default/bin/renew-hook [new file with mode: 0755]
cookbooks/letsencrypt/files/default/bin/upload [new file with mode: 0755]
cookbooks/letsencrypt/files/default/knife.rb [new file with mode: 0644]
cookbooks/letsencrypt/metadata.rb [new file with mode: 0644]
cookbooks/letsencrypt/recipes/default.rb [new file with mode: 0644]
cookbooks/letsencrypt/templates/default/apache.erb [new file with mode: 0644]
cookbooks/letsencrypt/templates/default/cron.erb [new file with mode: 0644]
cookbooks/letsencrypt/templates/default/request.erb [new file with mode: 0644]
cookbooks/ssl/.foodcritic
cookbooks/ssl/resources/certificate.rb [new file with mode: 0644]
cookbooks/ssl/templates/default/ssl.cnf.erb [new file with mode: 0644]
roles/ironbelly.rb
roles/letsencrypt.rb [new file with mode: 0644]
test/data_bags/accounts/letsencrypt.json [new file with mode: 0644]
test/data_bags/chef/keys.json [new file with mode: 0644]

index c5f8a69..c49fa23 100644 (file)
@@ -36,6 +36,15 @@ suites:
     run_list:
       - recipe[accounts::default]
       - role[forum]
+  - name: letsencrypt
+    run_list:
+      - recipe[accounts::default]
+      - recipe[apt::default]
+      - role[letsencrypt]
+    attributes:
+      apt:
+        sources:
+          - openstreetmap
   - name: munin
     run_list:
       - recipe[munin::default]
diff --git a/cookbooks/letsencrypt/.foodcritic b/cookbooks/letsencrypt/.foodcritic
new file mode 100644 (file)
index 0000000..d8973b2
--- /dev/null
@@ -0,0 +1,4 @@
+~FC001
+~FC003
+~FC064
+~FC065
diff --git a/cookbooks/letsencrypt/README.md b/cookbooks/letsencrypt/README.md
new file mode 100644 (file)
index 0000000..ad5f5e7
--- /dev/null
@@ -0,0 +1,4 @@
+# Letsencrypt Cookbook
+
+This cookbook provides lightweight resources to obtain and
+manage letsencrypt certficates.
diff --git a/cookbooks/letsencrypt/files/default/bin/renew b/cookbooks/letsencrypt/files/default/bin/renew
new file mode 100755 (executable)
index 0000000..d5b7232
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+/usr/bin/certbot renew \
+    --quiet \
+    --config-dir /srv/acme.openstreetmap.org/config \
+    --work-dir /srv/acme.openstreetmap.org/work \
+    --logs-dir /srv/acme.openstreetmap.org/logs \
+    --renew-hook /srv/acme.openstreetmap.org/renew-hook
diff --git a/cookbooks/letsencrypt/files/default/bin/renew-hook b/cookbooks/letsencrypt/files/default/bin/renew-hook
new file mode 100755 (executable)
index 0000000..26bf656
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+domains=($RENEWED_DOMAINS)
+
+exec /srv/acme.openstreetmap.org/bin/upload "${domains[0]}" "$RENEWED_LINEAGE"
diff --git a/cookbooks/letsencrypt/files/default/bin/upload b/cookbooks/letsencrypt/files/default/bin/upload
new file mode 100755 (executable)
index 0000000..56da629
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/ruby
+
+require "json"
+require "tempfile"
+
+domain = ARGV.shift
+directory = ARGV.shift
+
+bag = {
+  :id => domain,
+  :key => File.read(File.join(directory, "privkey.pem")),
+  :certificate => File.read(File.join(directory, "fullchain.pem"))
+}
+
+file = Tempfile.new(["letsencrypt", ".json"])
+
+file.puts JSON.generate(bag)
+file.close
+
+system("/usr/bin/knife", "data", "bag", "from", "file", "letsencrypt", file.path)
diff --git a/cookbooks/letsencrypt/files/default/knife.rb b/cookbooks/letsencrypt/files/default/knife.rb
new file mode 100644 (file)
index 0000000..24cb9ce
--- /dev/null
@@ -0,0 +1,11 @@
+node_name "letsencrypt"
+client_key "client.pem"
+validation_client_name "chef-validator"
+validation_key "/etc/chef/validation.pem"
+chef_server_url "https://chef.openstreetmap.org/organizations/openstreetmap"
+cache_type "BasicFile"
+cache_options :path => ".chef/checksums"
+cookbook_path ["cookbooks"]
+cookbook_copyright "OpenStreetMap Administrators"
+cookbook_email "admins@openstreetmap.org"
+cookbook_license "apachev2"
diff --git a/cookbooks/letsencrypt/metadata.rb b/cookbooks/letsencrypt/metadata.rb
new file mode 100644 (file)
index 0000000..69f1d32
--- /dev/null
@@ -0,0 +1,8 @@
+name              "letsencrypt"
+maintainer        "OpenStreetMap Administrators"
+maintainer_email  "admins@openstreetmap.org"
+license           "Apache 2.0"
+description       "Support for letsencrypt certificates"
+long_description  IO.read(File.join(File.dirname(__FILE__), "README.md"))
+version           "1.0.0"
+depends           "apache"
diff --git a/cookbooks/letsencrypt/recipes/default.rb b/cookbooks/letsencrypt/recipes/default.rb
new file mode 100644 (file)
index 0000000..f08fdf3
--- /dev/null
@@ -0,0 +1,149 @@
+#
+# Cookbook Name:: letsencrypt
+# Recipe:: default
+#
+# Copyright 2017, OpenStreetMap Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+include_recipe "apache::ssl"
+
+keys = data_bag_item("chef", "keys")
+
+package "certbot"
+package "ruby"
+
+directory "/etc/letsencrypt" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o755
+end
+
+directory "/var/lib/letsencrypt" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o755
+end
+
+directory "/var/log/letsencrypt" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o700
+end
+
+directory "/srv/acme.openstreetmap.org" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o755
+end
+
+directory "/srv/acme.openstreetmap.org/html" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o755
+end
+
+ssl_certificate "acme.openstreetmap.org" do
+  domains ["acme.openstreetmap.org", "acme.osm.org"]
+  notifies :reload, "service[apache2]"
+end
+
+apache_site "acme.openstreetmap.org" do
+  template "apache.erb"
+  directory "/srv/acme.openstreetmap.org"
+end
+
+directory "/srv/acme.openstreetmap.org/config" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o755
+end
+
+directory "/srv/acme.openstreetmap.org/work" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o755
+end
+
+directory "/srv/acme.openstreetmap.org/logs" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o700
+end
+
+directory "/srv/acme.openstreetmap.org/.chef" do
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o2775
+end
+
+file "/srv/acme.openstreetmap.org/.chef/client.pem" do
+  content keys["letsencrypt"].join("\n")
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o660
+end
+
+cookbook_file "/srv/acme.openstreetmap.org/.chef/knife.rb" do
+  source "knife.rb"
+  owner "letsencrypt"
+  group "letsencrypt"
+  mode 0o660
+end
+
+remote_directory "/srv/acme.openstreetmap.org/bin" do
+  source "bin"
+  owner "root"
+  group "root"
+  mode 0o755
+  files_owner "root"
+  files_group "root"
+  files_mode 0o755
+end
+
+directory "/srv/acme.openstreetmap.org/requests" do
+  owner "root"
+  group "root"
+  mode 0o755
+end
+
+certificates = search(:node, "letsencrypt:certificates").each_with_object({}) do |n, c|
+  c.merge!(n[:letsencrypt][:certificates])
+end
+
+certificates.each do |name, details|
+  template "/srv/acme.openstreetmap.org/requests/#{name}" do
+    source "request.erb"
+    owner "root"
+    group "letsencrypt"
+    mode 0o754
+    variables details
+  end
+
+  execute "/srv/acme.openstreetmap.org/requests/#{name}" do
+    action :nothing
+    command "/srv/acme.openstreetmap.org/requests/#{name}"
+    cwd "/srv/acme.openstreetmap.org"
+    user "letsencrypt"
+    group "letsencrypt"
+    subscribes :run, "template[/srv/acme.openstreetmap.org/requests/#{name}]"
+  end
+end
+
+template "/etc/cron.d/letsencrypt" do
+  source "cron.erb"
+  owner "root"
+  group "root"
+  mode 0o644
+end
diff --git a/cookbooks/letsencrypt/templates/default/apache.erb b/cookbooks/letsencrypt/templates/default/apache.erb
new file mode 100644 (file)
index 0000000..10d4c52
--- /dev/null
@@ -0,0 +1,31 @@
+# DO NOT EDIT - This file is being maintained by Chef
+
+<VirtualHost *:80>
+        ServerName acme.openstreetmap.org
+        ServerAlias acme.osm.org
+        ServerAdmin webmaster@openstreetmap.org
+
+        CustomLog /var/log/apache2/acme.openstreetmap.org-access.log combined
+        ErrorLog /var/log/apache2/acme.openstreetmap.org-error.log
+
+        DocumentRoot /srv/acme.openstreetmap.org/html
+</VirtualHost>
+
+<VirtualHost *:443>
+        ServerName acme.openstreetmap.org
+        ServerAlias acme.osm.org
+        ServerAdmin webmaster@openstreetmap.org
+
+        CustomLog /var/log/apache2/acme.openstreetmap.org-access.log combined
+        ErrorLog /var/log/apache2/acme.openstreetmap.orgĀ¬-error.log
+
+        SSLEngine on
+        SSLCertificateFile /etc/ssl/certs/acme.openstreetmap.org.pem
+        SSLCertificateKeyFile /etc/ssl/private/acme.openstreetmap.org.key
+
+        DocumentRoot /srv/acme.openstreetmap.org/html
+</VirtualHost>
+
+<Directory /srv/acme.openstreetmap.org/html>
+        Require all granted
+</Directory>
diff --git a/cookbooks/letsencrypt/templates/default/cron.erb b/cookbooks/letsencrypt/templates/default/cron.erb
new file mode 100644 (file)
index 0000000..88fd43f
--- /dev/null
@@ -0,0 +1 @@
+0 */12 * * * letsencrypt /srv/acme.openstreetmap.org/renew
diff --git a/cookbooks/letsencrypt/templates/default/request.erb b/cookbooks/letsencrypt/templates/default/request.erb
new file mode 100644 (file)
index 0000000..6d4c2b1
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# DO NOT EDIT - This file is being maintained by Chef
+
+/usr/bin/certbot certonly \
+    --non-interactive \
+    --config-dir /srv/acme.openstreetmap.org/config \
+    --work-dir /srv/acme.openstreetmap.org/work \
+    --logs-dir /srv/acme.openstreetmap.org/logs \
+    --email operations@osmfoundation.org \
+    --agree-tos \
+<% @domains.each do |domain| -%>
+    --domain <%= domain %> \
+<% end -%>
+    --webroot \
+    --webroot-path /srv/acme.openstreetmap.org/html
+
+/srv/acme.openstreetmap.org/bin/upload \
+    <%= @domains.first %> \
+    /srv/acme.openstreetmap.org/config/live/<%= @domains.first %>
index a085263..d8973b2 100644 (file)
@@ -1,3 +1,4 @@
 ~FC001
+~FC003
 ~FC064
 ~FC065
diff --git a/cookbooks/ssl/resources/certificate.rb b/cookbooks/ssl/resources/certificate.rb
new file mode 100644 (file)
index 0000000..92dbcc3
--- /dev/null
@@ -0,0 +1,96 @@
+#
+# Cookbook Name:: ssl
+# Resource:: ssl_certificate
+#
+# Copyright 2017, OpenStreetMap Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+default_action :create
+
+property :name, String
+property :domains, Array, :required => true
+property :fallback_certificate, String
+
+action :create do
+  node.default[:letsencrypt][:certificates][name] = {
+    :domains => domains
+  }
+
+  if letsencrypt
+    certificate = letsencrypt["certificate"]
+    key = letsencrypt["key"]
+  end
+
+  if certificate
+    file "/etc/ssl/certs/#{name}.pem" do
+      owner "root"
+      group "root"
+      mode 0o444
+      content certificate
+      backup false
+    end
+
+    file "/etc/ssl/private/#{name}.key" do
+      owner "root"
+      group "ssl-cert"
+      mode 0o440
+      content key
+      backup false
+    end
+  elsif fallback_certificate
+    link "/etc/ssl/certs/#{name}.pem" do
+      to "#{fallback_certificate}.pem"
+    end
+
+    link "/etc/ssl/private/#{name}.key" do
+      to "#{fallback_certificate}.key"
+    end
+  else
+    template "/tmp/#{name}.ssl.cnf" do
+      cookbook "ssl"
+      source "ssl.cnf.erb"
+      owner "root"
+      group "root"
+      mode 0o644
+      variables :domains => new_resource.domains
+      not_if do
+        ::File.exist?("/etc/ssl/certs/#{new_resource.name}.pem") && ::File.exist?("/etc/ssl/private/#{new_resource.name}.key")
+      end
+    end
+
+    execute "/etc/ssl/certs/#{name}.pem" do
+      command "openssl req -x509 -newkey rsa:2048 -keyout /etc/ssl/private/#{new_resource.name}.key -out /etc/ssl/certs/#{new_resource.name}.pem -days 365 -nodes -config /tmp/#{new_resource.name}.ssl.cnf"
+      user "root"
+      group "ssl-cert"
+      not_if do
+        ::File.exist?("/etc/ssl/certs/#{new_resource.name}.pem") && ::File.exist?("/etc/ssl/private/#{new_resource.name}.key")
+      end
+    end
+  end
+end
+
+action :delete do
+  file "/etc/ssl/certs/#{name}.pem" do
+    action :delete
+  end
+
+  file "/etc/ssl/private/#{name}.key" do
+    action :delete
+  end
+end
+
+def letsencrypt
+  @letsencrypt ||= search(:letsencrypt, "id:#{name}").first
+end
diff --git a/cookbooks/ssl/templates/default/ssl.cnf.erb b/cookbooks/ssl/templates/default/ssl.cnf.erb
new file mode 100644 (file)
index 0000000..64c3c08
--- /dev/null
@@ -0,0 +1,20 @@
+[req]
+prompt = no
+distinguished_name = req_dn
+x509_extensions = v3_req
+
+[req_dn]
+organizationName = OpenStreetMap
+commonName = <%= @domains.first %>
+emailAddress = operations@osmfoundation.org
+
+[v3_req]
+basicConstraints = CA:FALSE
+keyUsage = digitalSignature, keyEncipherment
+extendedKeyUsage = serverAuth, clientAuth
+subjectAltName = @alt_names
+
+[alt_names]
+<% @domains.each_with_index do |d, i| -%>
+DNS.<%= i + 1 %> = <%= d %>
+<% end -%>
index 3edf962..d5bb0ee 100644 (file)
@@ -135,6 +135,7 @@ run_list(
   "role[planet]",
   "role[planetdump]",
   "role[logstash]",
+  "role[letsencrypt]",
   "recipe[rsyncd]",
   "recipe[openvpn]",
   "recipe[git::server]",
diff --git a/roles/letsencrypt.rb b/roles/letsencrypt.rb
new file mode 100644 (file)
index 0000000..c0b389d
--- /dev/null
@@ -0,0 +1,16 @@
+name "letsencrypt"
+description "Role applied to all letsencrypt servers"
+
+default_attributes(
+  :accounts => {
+    :users => {
+      :letsencrypt => {
+        :status => :role
+      }
+    }
+  }
+)
+
+run_list(
+  "recipe[letsencrypt]"
+)
diff --git a/test/data_bags/accounts/letsencrypt.json b/test/data_bags/accounts/letsencrypt.json
new file mode 100644 (file)
index 0000000..2b4005a
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "id": "letsencrypt",
+  "uid": "526",
+  "comment": "Let's Encrypt",
+  "manage_home": false
+}
diff --git a/test/data_bags/chef/keys.json b/test/data_bags/chef/keys.json
new file mode 100644 (file)
index 0000000..762690d
--- /dev/null
@@ -0,0 +1,61 @@
+{
+  "id": "keys",
+  "git": [
+    "-----BEGIN RSA PRIVATE KEY-----",
+    "MIIEowIBAAKCAQEAmQecBvYZEP3owBge2k2rv4fkrUuxsdueVRArHdopTbMaej7H",
+    "FxGG/3ZhDN9P/okQVBE5NBnT1wGmeFVS7q+V+grPfp3C9tQiDBghdb5gAR+loSSm",
+    "7aYTYJ3ngy5DzMD7Jr8cpvl3x76Z/p3/JgFjR8nkjsk81vsj612drQ2YX3H3jvLp",
+    "ULAqxl6VDOv7Lx1SknE0PglTSemnq6O07R6zyLTc5ZUk1XD2KL1sRANluJg+q3ml",
+    "RllDNX0j8B3Sz0yttP5/GFRdc+82WS8+86eg2y2vQPpTo86mBwYzkdbG4WT2YIDw",
+    "w3J93TEL4CAuj0qm4DdDbhnMLkVtHE3uHBQfbQIDAQABAoIBAAXfX+J4gT/ArrTo",
+    "eA6immuwOFtaI0iTCAF1rGHp5Fnh/KsiS5ucBZU6IsFOtJAtDF5dhtd0Akgm/Q9z",
+    "HsqgHF5LGele/oNgYqoaJvaQxrhkUYPclzdZfzbe+Gp1VQ6/fSPgg6X4vtsAeoJl",
+    "58u6k+fGXKoKGPabDqbSoeFpOya2drudqVCZrM++5MchuuRFDKI+/gSsjJKUvAX5",
+    "l9mA1GvAI6pmYhIaSPe6ywLvtpDpRtRRaCg51IkIi+AdW7iuwaljhCTbo4u6Sp4m",
+    "ujCxY2n8j5l9QRnnT3Ax9289fgo6+IyRGQpijIBTMVI42ogBmUhC5xpJ+RNl55yy",
+    "LU5O1/UCgYEAxkny8NS2JT3swOYdeCNhy6ap7MoEkb78MLjuv7tb2/td16GVPcsJ",
+    "VryTX1O0dKH2K19GMm/RHfIUAGpZou7WbK9dKjA52nOOvipY5V0jMDfeTO9gSM/l",
+    "urMf0l4e5MK432E7/hxiCy6pBp6cIiHYkf1XGVe55+6BDbjJas3j0DcCgYEAxZGB",
+    "WhLUUVK63DC690oIl2F8IbktclZfgka41/iWkoa+MUkViSQ5GQ8nOg8TpjWx4Q4V",
+    "4Z9C9ALeNKhZ1XvYchHTqcLE3aiMnF/3ZY5qxzjxX3aq+yLbPqRFsS/1pA58k6Jf",
+    "Jc2vJiYqNPPvQfuhypgku7pzf/LrDS8EDf6tE3sCgYAFYe6BURTcr/CkT9rO7w7x",
+    "i0WjktxK5IdN/0cj1z8oGouylcVKVx+axiWt+cS1Qcw/4ycxqU1g5bhbRofGX3tc",
+    "meoKgiKf5nEigl3FZCDXZzzWk8zmTRZsWf5sJHfsN8jy713EiRq0OQEHl/ifCJIr",
+    "bFgX7QSz4gqIx9JX3tznQwKBgCDeRy3MCiSJZerx9HjliS5eGn+lxgjKk9MhnujX",
+    "Q32XCxc5+Go7a4BexADltzgkoLY3WK6Th1j/DSanh2J72xOHIbaRX50cyF/Pm2H6",
+    "4orIT2e5X1KuhtkSDUIgH3aurk0Fa1znribjnIv4tSo+CbmhvCK7LzHvIOmtk7gc",
+    "UYD3AoGBAKxnmdIw+07fThOjpMIZ3BQKF3lRDNG4YWDR/JKBtfN6km64yuBxe26O",
+    "z12O6hHdn2qRwVTAB9Q/2IgXad7erLpHMkxau+buOdUDYplRsv1rlo57ZUQn+pOD",
+    "gUvWf4G25mBqtPGDlsb5OufkXc3NfrsDj/nAlZ3Wk//QXbB3xRd7",
+    "-----END RSA PRIVATE KEY-----"
+  ],
+  "letsencrypt": [
+    "-----BEGIN RSA PRIVATE KEY-----",
+    "MIIEowIBAAKCAQEAuUPGzcK1w1NRZtLV1MlbK2D5+Qw/GBn1h/dpr/YjYDxzqyaS",
+    "4OPXwfwl2NJ6VXGzPnmzgkQaKu1qxpmZ/jqQ26+XQPPrnrd4kBscNvGEStbkwrsp",
+    "mz5YsGfyG3+Uz625C5OOyXmkRXq7J2gzCrmO5u+RWs0Aj6As21G18eA61QPRthzz",
+    "P5R4/Un+zN4+kEOTVVYBTop45QPtgi8opc3OZcla2bZbCxuqvU0bb/67mUo+ndjx",
+    "NtSmKC4wl2q8wIfblycC+abvEIG1Jog64G0fbAGG5oz8Ol8W6q5B2tfpicpigETk",
+    "Ob4dp5bXcALCll/IyUxVxjQURqM2zN+ZPx33uwIDAQABAoIBAQCOFNmhscMeIoba",
+    "ObV+NFJ0KTJseqTkwfvYo7ltFnK4+oOm7bVVPceZYNxRtdHWN5XEwycVL092PpBV",
+    "8TT1kUrJAJgaWzcHiSOwOOphhMX1c2sLoOhew+jWmVFHH4gr4cp5g1fNUjnWgzKH",
+    "HVWP0xEyMOaj4Xadr7TXGopUDqhv9egN1Sw7091Njk+b5i5waJ7LTfQXDJ53rcoJ",
+    "1QhoLn8kxeBea/gRQ3FLpghPeEqXnjnyvPBMk3mDiDtwubUkuEs1P0c9znfaRW+m",
+    "EGFQUAOYnBGOsGkty+0Na8U+Uuj1uGODWPHXQr9p8+AZR3fF7e/9efBvlVAH5KY7",
+    "4vaYhOtRAoGBAOkfTyaZK2vT7nSC1ubgGJduxK/8kifP+whfiUlvGEMIzUDFbqST",
+    "L1hCjxOy4FQg+XY+8vx5iwluZhb1sVp4BEeepTPmqDp1AyHMRYy3pZz9zmOYbysN",
+    "RpglX2GO6XLUnxSFCjf+XcaVzLiVU6XWDYBvvYGBNYLO8bhJ97rwrOcjAoGBAMty",
+    "JoqmTDzAk+8BV3cci5RbaB7AtmKNEeQ3Ih6Rz3fb8rqxgHnyhNtasWDe84RvykSU",
+    "LlfpHhVc9zbePkrGZTtZAmzc0qjOqp5lzAHI1UMdvacEVV3UZgHsoVHhFMPEj0bk",
+    "hoh5opy2lyu9YkM8LJ9RV7Vua6AQcPmsJBh0mAKJAoGAAQ9dYsWLhv/9s5XsuDwI",
+    "oJemWU6Cs5+kepNEoorYx2VA2ayMJj9tFa+nyuUjU/6aY8lBfZhn43EXEb+oQMsO",
+    "6ex0v9mqpilmDD9LiapEHISi7Z0B1GZJDeQNnPnzYcxJtOQt+bc0YfTIa4ZyTOy+",
+    "PvlDGVWnEqMyQi5D7BuwDZUCgYBSFWprgpE76c9GHVp22nOOlhq6XbK4rIZNd9ky",
+    "UE5O49VZcgiOK0VjY4IxvYKvKpOHe+n+2jWjFPFBmAW2EboCafVKiwYLyeaZJiVb",
+    "ivZQsA02986hnvLRT/H+oTvJiOLuDYIiSkFLzXfM1ApzajHuzdj/gN+3oyqR8dxW",
+    "aaRzUQKBgEw7U0vPikteurPmdmBkxYZTnK3o0uhfbiJTPuaSwahlRrxXYolndDoj",
+    "IvQrZQp5W1+lXpSbAfJCeO7LxH4kjXtboIGUrBviiTALSVnmF8vX1qd1vEVAFh6I",
+    "Q8pw9G2XqjNjlY/3mL4LLCPwjKlLsZDAeYn9AfUffbQiT8ZjhXMv",
+    "-----END RSA PRIVATE KEY-----"
+  ]
+}