{"action":"created","issue":{"url":"https://api.github.com/repos/schubergphilis/chef-acme/issues/63","repository_url":"https://api.github.com/repos/schubergphilis/chef-acme","labels_url":"https://api.github.com/repos/schubergphilis/chef-acme/issues/63/labels{/name}","comments_url":"https://api.github.com/repos/schubergphilis/chef-acme/issues/63/comments","events_url":"https://api.github.com/repos/schubergphilis/chef-acme/issues/63/events","html_url":"https://github.com/schubergphilis/chef-acme/pull/63","id":219147155,"number":63,"title":"initial implementation of persistence [RFC/WIP]","user":{"login":"rmoriz","id":1527,"avatar_url":"https://avatars0.githubusercontent.com/u/1527?v=3","gravatar_id":"","url":"https://api.github.com/users/rmoriz","html_url":"https://github.com/rmoriz","followers_url":"https://api.github.com/users/rmoriz/followers","following_url":"https://api.github.com/users/rmoriz/following{/other_user}","gists_url":"https://api.github.com/users/rmoriz/gists{/gist_id}","starred_url":"https://api.github.com/users/rmoriz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rmoriz/subscriptions","organizations_url":"https://api.github.com/users/rmoriz/orgs","repos_url":"https://api.github.com/users/rmoriz/repos","events_url":"https://api.github.com/users/rmoriz/events{/privacy}","received_events_url":"https://api.github.com/users/rmoriz/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":6,"created_at":"2017-04-04T05:53:51Z","updated_at":"2017-04-24T09:35:21Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/schubergphilis/chef-acme/pulls/63","html_url":"https://github.com/schubergphilis/chef-acme/pull/63","diff_url":"https://github.com/schubergphilis/chef-acme/pull/63.diff","patch_url":"https://github.com/schubergphilis/chef-acme/pull/63.patch"},"body":"(This is currently a RFC which I just hacked together.)\r\n\r\n## Motivation:\r\n\r\nIn complex setups, when you have more than one front-facing httpd, like in anycast/cdn setups, you have the following problems with acme workflows:\r\n\r\n- initial bootstrap needs certificates (self-signed, no problem)\r\n- only one node should start the acme verification process\r\n- acme/LE needs to be directed to the right node every time\r\n- additional node bootstrapping and renewal needs a way to distribute the key/cert\r\n\r\nI've seen many suggestions like using s3, `syncthing`, `rsync`… however I don't like the complexity and more single points of failure.\r\n\r\n## My implementation\r\n\r\n- uses chef's data bags, with or without encryption (strongly preferred)\r\n- needs a chef-server or chef-zero setup ([knife-zero](https://github.com/higanworks/knife-zero) works really well)\r\n\r\n### Limitation\r\n\r\nThe master client needs the correct permission to *create*/*upload* data bags to the chef server. This may have security implications. \r\n\r\nOne must configure one and only one node which acts as master. This can be a simple node attribute, or some DSL method like `tagged?`. fail-over must be implemented manually or by automatically updating attributes on the chef-server by a 3rd party (e.g. monitoring). \r\n\r\nKey/Cert on workers may lag one converge cycle, this can be reduced by triggering converges on workers with tools like chef's \"push jobs\".\r\n\r\n\r\n## Usage:\r\n\r\n### Chef\r\n\r\nExample recipe code:\r\n\r\n```ruby\r\n\r\n\r\n# action :load checks for an existing data bag item and renders the files\r\n#\r\nacme_persistence 'example.com' do\r\n  master node['acme']['master']  # this should be true only on the master node\r\n  data_bag_name 'acme-certificates'\r\n  alt_names %w(www.example.com example.com)\r\n  key       '/etc/ssl/private/example.com.key'\r\n  fullchain '/etc/ssl/certs/example.com.crt'\r\n  action :load\r\n\r\n  notifies :reload, 'service[nginx]'\r\nend\r\n\r\nacme_selfsigned 'example.com' do\r\n  key '/etc/ssl/private/example.com.key'\r\n  crt '/etc/ssl/certs/example.com.crt'\r\n\r\n  not_if  'test -f /etc/ssl/certs/example.com.key && test -f /etc/ssl/certs/example.com.crt'\r\nend\r\n\r\n(configure nginx)\r\n\r\nacme_certificate 'example.com' do\r\n  alt_names %w(www.example.com example.com)\r\n  key      '/etc/ssl/private/example.com.key'\r\n  fullchain '/etc/ssl/certs/example.com.crt'\r\n  method   'http'\r\n  wwwroot  '/var/www/acme'\r\n\r\n  only_if { node['acme']['master'] }\r\n  notifies :save,   'acme_persistence[example.com]', :immediately # this will trigger the data bag item upload\r\n  notifies :reload, 'service[nginx]', :immediately\r\nend\r\n\r\n```\r\n\r\n### on the master node with `node['acme']['master']` set to true\r\n\r\n##### first run\r\n- no data bag item will be found\r\n- acme_selfsigned will create a selfsigned certificate\r\n- acme_certificate will do the acme work and trigger `save` to `acme_persistence`.\r\n- `acme_persistence` uploads data to server.\r\n\r\n##### subsequent runs/ renew\r\n- data bag data found but will only be written if existing cert is self-signed or older\r\n- acme_certificate will check expiration, re-keying, and trigger `save` to `acme_persistence`\r\n- `acme_persistence` uploads data to server.\r\n\r\n### on workers, without `node['acme']['master']`\r\n\r\n##### first run\r\n- `acme_persistence` does not find certificates in the data bag\r\n-  `acme_selfsigned` will create dummies so the webserver can start. \r\n\r\n##### subsequent runs/ renew\r\n- `acme_persistence` finds a data bag item and renders out the data and triggers a webserver reload (same logic as in the master)\r\n\r\n## Webserver\r\n\r\nWorkers must proxy acme verification requests to the `.well-known` URL to the master node. This can easily be scripted inside a virtual host template and triggered by the same source as above (e.g. a node attribute)\r\n\r\ne.g.\r\n```\r\n  # /etc/nginx/conf.d/upstream.conf\r\n  upstream acme_master {\r\n    server unique-address.webserver.example.com:443;\r\n  }\r\n```\r\n\r\n```\r\n # vhost-template.conf.erb\r\n...\r\n\r\nlocation ^~ /.well-known/acme-challenge/ {\r\n  default_type \"text/plain\";\r\n\r\n  <% if @acme_master %>\r\n  root /var/www/acme;\r\n  <% else %>\r\n  proxy_pass https://acme_master;\r\n  <% end %>\r\n}\r\n\r\nlocation = /.well-known/acme-challenge/ {\r\n  return 404;\r\n}\r\n...\r\n```\r\n\r\n\r\n\r\n\r\n\r\nrelated: https://github.com/schubergphilis/chef-acme/issues/15\r\n\r\n\r\n\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/schubergphilis/chef-acme/issues/comments/296595089","html_url":"https://github.com/schubergphilis/chef-acme/pull/63#issuecomment-296595089","issue_url":"https://api.github.com/repos/schubergphilis/chef-acme/issues/63","id":296595089,"user":{"login":"thoutenbos","id":5472093,"avatar_url":"https://avatars0.githubusercontent.com/u/5472093?v=3","gravatar_id":"","url":"https://api.github.com/users/thoutenbos","html_url":"https://github.com/thoutenbos","followers_url":"https://api.github.com/users/thoutenbos/followers","following_url":"https://api.github.com/users/thoutenbos/following{/other_user}","gists_url":"https://api.github.com/users/thoutenbos/gists{/gist_id}","starred_url":"https://api.github.com/users/thoutenbos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thoutenbos/subscriptions","organizations_url":"https://api.github.com/users/thoutenbos/orgs","repos_url":"https://api.github.com/users/thoutenbos/repos","events_url":"https://api.github.com/users/thoutenbos/events{/privacy}","received_events_url":"https://api.github.com/users/thoutenbos/received_events","type":"User","site_admin":false},"created_at":"2017-04-24T09:35:21Z","updated_at":"2017-04-24T09:35:21Z","body":"Now that we have Chef 13 support and the build fixes this should be able to go to master. Would you be able to add the README and integration tests to make it ready ?"}}
{"actor":{"display_login":"thoutenbos"}}