Repository navigation
Expand file tree
/
Copy pathletsencrypt.lua
More file actions
187 lines (177 loc) · 6.12 KB
/
Copy pathletsencrypt.lua
File metadata and controls
187 lines (177 loc) · 6.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
local cjson = require "cjson"
local class = require "middleclass"
local plugin = require "bunkerweb.plugin"
local ssl = require "ngx.ssl"
local utils = require "bunkerweb.utils"
local letsencrypt = class("letsencrypt", plugin)
local ngx = ngx
local ERR = ngx.ERR
local NOTICE = ngx.NOTICE
local OK = ngx.OK
local HTTP_NOT_FOUND = ngx.HTTP_NOT_FOUND
local HTTP_OK = ngx.HTTP_OK
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
local parse_pem_cert = ssl.parse_pem_cert
local parse_pem_priv_key = ssl.parse_pem_priv_key
local ssl_server_name = ssl.server_name
local get_variable = utils.get_variable
local get_multiple_variables = utils.get_multiple_variables
local has_variable = utils.has_variable
local read_files = utils.read_files
local open = io.open
local sub = string.sub
local match = string.match
local decode = cjson.decode
local execute = os.execute
local remove = os.remove
function letsencrypt:initialize(ctx)
-- Call parent initialize
plugin.initialize(self, "letsencrypt", ctx)
end
function letsencrypt:set()
local https_configured = self.variables["AUTO_LETS_ENCRYPT"]
if https_configured == "yes" then
self.ctx.bw.https_configured = "yes"
end
return self:ret(true, "set https_configured to " .. https_configured)
end
function letsencrypt:init()
local ret_ok, ret_err = true, "success"
if has_variable("AUTO_LETS_ENCRYPT", "yes") then
local multisite, err = get_variable("MULTISITE", false)
if not multisite then
return self:ret(false, "can't get MULTISITE variable : " .. err)
end
if multisite == "yes" then
local vars
vars, err = get_multiple_variables({ "AUTO_LETS_ENCRYPT", "SERVER_NAME" })
if not vars then
return self:ret(false, "can't get AUTO_LETS_ENCRYPT variables : " .. err)
end
for server_name, multisite_vars in pairs(vars) do
if multisite_vars["AUTO_LETS_ENCRYPT"] == "yes" and server_name ~= "global" then
local check, data = read_files({
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name .. "/fullchain.pem",
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name .. "/privkey.pem",
})
if not check then
self.logger:log(ERR, "error while reading files : " .. data)
ret_ok = false
ret_err = "error reading files"
else
check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
if not check then
self.logger:log(ERR, "error while loading data : " .. err)
ret_ok = false
ret_err = "error loading data"
end
end
end
end
else
local server_name
server_name, err = get_variable("SERVER_NAME", false)
if not server_name then
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
end
local check, data = read_files({
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name:match("%S+") .. "/fullchain.pem",
"/var/cache/bunkerweb/letsencrypt/etc/live/" .. server_name:match("%S+") .. "/privkey.pem",
})
if not check then
self.logger:log(ERR, "error while reading files : " .. data)
ret_ok = false
ret_err = "error reading files"
else
check, err = self:load_data(data, server_name)
if not check then
self.logger:log(ERR, "error while loading data : " .. err)
ret_ok = false
ret_err = "error loading data"
end
end
end
else
ret_err = "let's encrypt is not used"
end
return self:ret(ret_ok, ret_err)
end
function letsencrypt:ssl_certificate()
local server_name, err = ssl_server_name()
if not server_name then
return self:ret(false, "can't get server_name : " .. err)
end
local data
data, err = self.datastore:get("plugin_letsencrypt_" .. server_name, true)
if not data and err ~= "not found" then
return self:ret(false, "error while getting plugin_letsencrypt_" .. server_name .. " from datastore : " .. err)
elseif data then
return self:ret(true, "certificate/key data found", data)
end
return self:ret(true, "let's encrypt is not used")
end
function letsencrypt:load_data(data, server_name)
-- Load certificate
local cert_chain, err = parse_pem_cert(data[1])
if not cert_chain then
return false, "error while parsing pem cert : " .. err
end
-- Load key
local priv_key
priv_key, err = parse_pem_priv_key(data[2])
if not priv_key then
return false, "error while parsing pem priv key : " .. err
end
-- Cache data
for key in server_name:gmatch("%S+") do
local cache_key = "plugin_letsencrypt_" .. key
local ok
ok, err = self.datastore:set(cache_key, { cert_chain, priv_key }, nil, true)
if not ok then
return false, "error while setting data into datastore : " .. err
end
end
return true
end
function letsencrypt:access()
if sub(self.ctx.bw.uri, 1, string.len("/.well-known/acme-challenge/")) == "/.well-known/acme-challenge/" then
self.logger:log(NOTICE, "got a visit from Let's Encrypt, let's whitelist it")
return self:ret(true, "visit from LE", OK)
end
return self:ret(true, "success")
end
-- luacheck: ignore 212
function letsencrypt:api()
if
not match(self.ctx.bw.uri, "^/lets%-encrypt/challenge$")
or (self.ctx.bw.request_method ~= "POST" and self.ctx.bw.request_method ~= "DELETE")
then
return self:ret(false, "success")
end
local acme_folder = "/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/"
local ngx_req = ngx.req
ngx_req.read_body()
local ret, data = pcall(decode, ngx_req.get_body_data())
if not ret then
return self:ret(true, "json body decoding failed", HTTP_BAD_REQUEST)
end
execute("mkdir -p " .. acme_folder)
if self.ctx.bw.request_method == "POST" then
local file, err = open(acme_folder .. data.token, "w+")
if not file then
return self:ret(true, "can't write validation token : " .. err, HTTP_INTERNAL_SERVER_ERROR)
end
file:write(data.validation)
file:close()
return self:ret(true, "validation token written", HTTP_OK)
elseif self.ctx.bw.request_method == "DELETE" then
local ok, err = remove(acme_folder .. data.token)
if not ok then
return self:ret(true, "can't remove validation token : " .. err, HTTP_INTERNAL_SERVER_ERROR)
end
return self:ret(true, "validation token removed", HTTP_OK)
end
return self:ret(true, "unknown request", HTTP_NOT_FOUND)
end
return letsencrypt