pylego 0.1.32__py3-none-any.whl → 0.1.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pylego/go.mod +81 -96
- pylego/go.sum +203 -491
- pylego/lego.go +211 -20
- pylego/lego.so +0 -0
- pylego/pylego.py +89 -5
- {pylego-0.1.32.dist-info → pylego-0.1.34.dist-info}/METADATA +24 -1
- pylego-0.1.34.dist-info/RECORD +11 -0
- {pylego-0.1.32.dist-info → pylego-0.1.34.dist-info}/WHEEL +1 -1
- pylego-0.1.32.dist-info/RECORD +0 -11
- {pylego-0.1.32.dist-info → pylego-0.1.34.dist-info}/licenses/LICENSE +0 -0
- {pylego-0.1.32.dist-info → pylego-0.1.34.dist-info}/top_level.txt +0 -0
pylego/lego.go
CHANGED
|
@@ -11,9 +11,12 @@ import (
|
|
|
11
11
|
"encoding/pem"
|
|
12
12
|
"errors"
|
|
13
13
|
"fmt"
|
|
14
|
+
"net"
|
|
14
15
|
"os"
|
|
16
|
+
"strings"
|
|
15
17
|
"time"
|
|
16
18
|
|
|
19
|
+
"github.com/go-acme/lego/v4/acme"
|
|
17
20
|
"github.com/go-acme/lego/v4/certcrypto"
|
|
18
21
|
"github.com/go-acme/lego/v4/certificate"
|
|
19
22
|
"github.com/go-acme/lego/v4/challenge/dns01"
|
|
@@ -24,6 +27,22 @@ import (
|
|
|
24
27
|
"github.com/go-acme/lego/v4/registration"
|
|
25
28
|
)
|
|
26
29
|
|
|
30
|
+
// Error code constants
|
|
31
|
+
const (
|
|
32
|
+
ErrInvalidArguments = "invalid_arguments"
|
|
33
|
+
ErrInvalidEnvironment = "invalid_environment"
|
|
34
|
+
ErrCertificateRequestFailed = "certificate_request_failed"
|
|
35
|
+
ErrInvalidPrivateKey = "invalid_private_key"
|
|
36
|
+
ErrKeyGenerationFailed = "key_generation_failed"
|
|
37
|
+
ErrLegoClientCreationFailed = "lego_client_creation_failed"
|
|
38
|
+
ErrDNSProviderFailed = "dns_provider_failed"
|
|
39
|
+
ErrAccountRegistrationFailed = "account_registration_failed"
|
|
40
|
+
ErrInvalidCSR = "invalid_csr"
|
|
41
|
+
ErrCertificateObtainFailed = "certificate_obtain_failed"
|
|
42
|
+
ErrNetworkError = "network_error"
|
|
43
|
+
ErrMarshalingFailed = "marshaling_failed"
|
|
44
|
+
)
|
|
45
|
+
|
|
27
46
|
type LegoInputArgs struct {
|
|
28
47
|
Email string `json:"email"`
|
|
29
48
|
PrivateKey string `json:"private_key,omitempty"`
|
|
@@ -48,28 +67,203 @@ type Metadata struct {
|
|
|
48
67
|
Domain string `json:"domain"`
|
|
49
68
|
}
|
|
50
69
|
|
|
70
|
+
type Subproblem struct {
|
|
71
|
+
Type string `json:"type"` // Error type URN
|
|
72
|
+
Detail string `json:"detail"` // Human-readable message
|
|
73
|
+
Identifier Identifier `json:"identifier,omitempty"` // The identifier that caused this subproblem
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type Identifier struct {
|
|
77
|
+
Type string `json:"type"` // "dns" or "ip"
|
|
78
|
+
Value string `json:"value"` // Domain name or IP address
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type ErrorResponse struct {
|
|
82
|
+
Type string `json:"type"` // "acme" for CA server errors, "lego" for everything else
|
|
83
|
+
Code string `json:"code"` // Error code or category
|
|
84
|
+
Status *int `json:"status,omitempty"` // HTTP status if applicable (ACME errors)
|
|
85
|
+
Detail string `json:"detail"` // Human-readable message
|
|
86
|
+
ACMEType string `json:"acme_type,omitempty"` // Full ACME URN if applicable
|
|
87
|
+
Subproblems []Subproblem `json:"subproblems,omitempty"` // Detailed subproblems from ACME errors
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type LegoResponse struct {
|
|
91
|
+
Success bool `json:"success"`
|
|
92
|
+
Error *ErrorResponse `json:"error,omitempty"`
|
|
93
|
+
Data *LegoOutputResponse `json:"data,omitempty"`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func isNetworkError(err error) bool {
|
|
97
|
+
if err == nil {
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
var (
|
|
101
|
+
netErr net.Error
|
|
102
|
+
dnsErr *net.DNSError
|
|
103
|
+
opErr *net.OpError
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
switch {
|
|
107
|
+
case errors.As(err, &netErr):
|
|
108
|
+
return true
|
|
109
|
+
case errors.As(err, &dnsErr):
|
|
110
|
+
return true
|
|
111
|
+
case errors.As(err, &opErr):
|
|
112
|
+
return true
|
|
113
|
+
default:
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
func extractSubproblems(problemDetails *acme.ProblemDetails) []Subproblem {
|
|
119
|
+
var subproblems []Subproblem
|
|
120
|
+
for _, sub := range problemDetails.SubProblems {
|
|
121
|
+
subCode := "unknown"
|
|
122
|
+
if sub.Type != "" {
|
|
123
|
+
parts := strings.Split(sub.Type, ":")
|
|
124
|
+
if len(parts) > 0 {
|
|
125
|
+
subCode = parts[len(parts)-1]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
subproblems = append(subproblems, Subproblem{
|
|
129
|
+
Type: subCode,
|
|
130
|
+
Detail: sub.Detail,
|
|
131
|
+
Identifier: Identifier{
|
|
132
|
+
Type: sub.Identifier.Type,
|
|
133
|
+
Value: sub.Identifier.Value,
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
return subproblems
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
func wrapError(err error, context string) *ErrorResponse {
|
|
141
|
+
if err == nil {
|
|
142
|
+
return nil
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
var ctxErr *contextError
|
|
146
|
+
if errors.As(err, &ctxErr) {
|
|
147
|
+
context = ctxErr.context
|
|
148
|
+
err = ctxErr.original
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
var problemDetails *acme.ProblemDetails
|
|
152
|
+
if errors.As(err, &problemDetails) {
|
|
153
|
+
code := "unknown"
|
|
154
|
+
if problemDetails.Type != "" {
|
|
155
|
+
parts := strings.Split(problemDetails.Type, ":")
|
|
156
|
+
if len(parts) > 0 {
|
|
157
|
+
code = parts[len(parts)-1]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
status := problemDetails.HTTPStatus
|
|
161
|
+
|
|
162
|
+
return &ErrorResponse{
|
|
163
|
+
Type: "acme",
|
|
164
|
+
Code: code,
|
|
165
|
+
Status: &status,
|
|
166
|
+
Detail: problemDetails.Detail,
|
|
167
|
+
ACMEType: problemDetails.Type,
|
|
168
|
+
Subproblems: extractSubproblems(problemDetails),
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if isNetworkError(err) {
|
|
173
|
+
return &ErrorResponse{
|
|
174
|
+
Type: "lego",
|
|
175
|
+
Code: ErrNetworkError,
|
|
176
|
+
Detail: err.Error(),
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return &ErrorResponse{
|
|
181
|
+
Type: "lego",
|
|
182
|
+
Code: context,
|
|
183
|
+
Detail: err.Error(),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
func buildErrorResponse(err error, context string) *C.char {
|
|
188
|
+
response := LegoResponse{
|
|
189
|
+
Success: false,
|
|
190
|
+
Error: wrapError(err, context),
|
|
191
|
+
}
|
|
192
|
+
responseJSON, marshalErr := json.Marshal(response)
|
|
193
|
+
if marshalErr == nil {
|
|
194
|
+
return C.CString(string(responseJSON))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fallbackResponse := LegoResponse{
|
|
198
|
+
Success: false,
|
|
199
|
+
Error: &ErrorResponse{
|
|
200
|
+
Type: "lego",
|
|
201
|
+
Code: ErrMarshalingFailed,
|
|
202
|
+
Detail: marshalErr.Error(),
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
fallbackJSON, _ := json.Marshal(fallbackResponse)
|
|
206
|
+
|
|
207
|
+
return C.CString(string(fallbackJSON))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
func buildSuccessResponse(data *LegoOutputResponse) *C.char {
|
|
211
|
+
response := LegoResponse{
|
|
212
|
+
Success: true,
|
|
213
|
+
Data: data,
|
|
214
|
+
}
|
|
215
|
+
responseJSON, marshalErr := json.Marshal(response)
|
|
216
|
+
if marshalErr == nil {
|
|
217
|
+
return C.CString(string(responseJSON))
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
fallbackResponse := LegoResponse{
|
|
221
|
+
Success: false,
|
|
222
|
+
Error: &ErrorResponse{
|
|
223
|
+
Type: "lego",
|
|
224
|
+
Code: ErrMarshalingFailed,
|
|
225
|
+
Detail: marshalErr.Error(),
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
fallbackJSON, _ := json.Marshal(fallbackResponse)
|
|
229
|
+
|
|
230
|
+
return C.CString(string(fallbackJSON))
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
type contextError struct {
|
|
234
|
+
original error
|
|
235
|
+
context string
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
func (e *contextError) Error() string {
|
|
239
|
+
return e.original.Error()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
func (e *contextError) Unwrap() error {
|
|
243
|
+
return e.original
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
func wrapWithContext(err error, context string) error {
|
|
247
|
+
return &contextError{original: err, context: context}
|
|
248
|
+
}
|
|
249
|
+
|
|
51
250
|
//export RunLegoCommand
|
|
52
251
|
func RunLegoCommand(message *C.char) *C.char {
|
|
53
252
|
CLIArgs, err := extractArguments(C.GoString(message))
|
|
54
253
|
if err != nil {
|
|
55
|
-
return
|
|
254
|
+
return buildErrorResponse(err, ErrInvalidArguments)
|
|
56
255
|
}
|
|
57
256
|
for k, v := range CLIArgs.Env {
|
|
58
257
|
if err := os.Setenv(k, v); err != nil {
|
|
59
|
-
return
|
|
258
|
+
return buildErrorResponse(err, ErrInvalidEnvironment)
|
|
60
259
|
}
|
|
61
260
|
|
|
62
261
|
}
|
|
63
262
|
certificate, err := requestCertificate(CLIArgs.Email, CLIArgs.PrivateKey, CLIArgs.Server, CLIArgs.CSR, CLIArgs.Plugin, CLIArgs.DNSPropagationWait)
|
|
64
263
|
if err != nil {
|
|
65
|
-
return
|
|
264
|
+
return buildErrorResponse(err, ErrCertificateRequestFailed)
|
|
66
265
|
}
|
|
67
|
-
|
|
68
|
-
if err != nil {
|
|
69
|
-
return C.CString(fmt.Sprint("error: coudn't build response message: ", err))
|
|
70
|
-
}
|
|
71
|
-
return_message_ptr := C.CString(string(response_json))
|
|
72
|
-
return return_message_ptr
|
|
266
|
+
return buildSuccessResponse(certificate)
|
|
73
267
|
}
|
|
74
268
|
|
|
75
269
|
func requestCertificate(email, privateKeyPem, server, csr, plugin string, propagationWait int) (*LegoOutputResponse, error) {
|
|
@@ -77,13 +271,13 @@ func requestCertificate(email, privateKeyPem, server, csr, plugin string, propag
|
|
|
77
271
|
if privateKeyPem != "" {
|
|
78
272
|
parsedKey, err := certcrypto.ParsePEMPrivateKey([]byte(privateKeyPem))
|
|
79
273
|
if err != nil {
|
|
80
|
-
return nil,
|
|
274
|
+
return nil, wrapWithContext(err, ErrInvalidPrivateKey)
|
|
81
275
|
}
|
|
82
276
|
privateKey = parsedKey
|
|
83
277
|
} else {
|
|
84
278
|
generatedKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
85
279
|
if err != nil {
|
|
86
|
-
return nil,
|
|
280
|
+
return nil, wrapWithContext(err, ErrKeyGenerationFailed)
|
|
87
281
|
}
|
|
88
282
|
privateKey = generatedKey
|
|
89
283
|
}
|
|
@@ -98,27 +292,27 @@ func requestCertificate(email, privateKeyPem, server, csr, plugin string, propag
|
|
|
98
292
|
|
|
99
293
|
client, err := lego.NewClient(config)
|
|
100
294
|
if err != nil {
|
|
101
|
-
return nil,
|
|
295
|
+
return nil, wrapWithContext(err, ErrLegoClientCreationFailed)
|
|
102
296
|
}
|
|
103
297
|
|
|
104
298
|
err = configureClientChallenges(client, plugin, propagationWait)
|
|
105
299
|
if err != nil {
|
|
106
|
-
return nil,
|
|
300
|
+
return nil, wrapWithContext(err, ErrDNSProviderFailed)
|
|
107
301
|
}
|
|
108
302
|
|
|
109
303
|
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
|
110
304
|
if err != nil {
|
|
111
|
-
return nil,
|
|
305
|
+
return nil, wrapWithContext(err, ErrAccountRegistrationFailed)
|
|
112
306
|
}
|
|
113
307
|
user.Registration = reg
|
|
114
308
|
|
|
115
309
|
block, _ := pem.Decode([]byte(csr))
|
|
116
310
|
if block == nil || block.Type != "CERTIFICATE REQUEST" {
|
|
117
|
-
return nil, errors.New("failed to decode PEM block
|
|
311
|
+
return nil, wrapWithContext(errors.New("failed to decode PEM block"), ErrInvalidCSR)
|
|
118
312
|
}
|
|
119
313
|
csrObject, err := x509.ParseCertificateRequest(block.Bytes)
|
|
120
314
|
if err != nil {
|
|
121
|
-
return nil,
|
|
315
|
+
return nil, wrapWithContext(err, ErrInvalidCSR)
|
|
122
316
|
}
|
|
123
317
|
request := certificate.ObtainForCSRRequest{
|
|
124
318
|
CSR: csrObject,
|
|
@@ -126,7 +320,7 @@ func requestCertificate(email, privateKeyPem, server, csr, plugin string, propag
|
|
|
126
320
|
}
|
|
127
321
|
certificates, err := client.Certificate.ObtainForCSR(request)
|
|
128
322
|
if err != nil {
|
|
129
|
-
return nil,
|
|
323
|
+
return nil, wrapWithContext(err, ErrCertificateObtainFailed)
|
|
130
324
|
}
|
|
131
325
|
|
|
132
326
|
return &LegoOutputResponse{
|
|
@@ -160,9 +354,6 @@ func configureClientChallenges(client *lego.Client, plugin string, propagationWa
|
|
|
160
354
|
return errors.Join(fmt.Errorf("couldn't create %s provider: ", plugin), err)
|
|
161
355
|
}
|
|
162
356
|
var wait time.Duration
|
|
163
|
-
if propagationWait < 0 {
|
|
164
|
-
return fmt.Errorf("DNS_PROPAGATION_WAIT cannot be negative: %d", propagationWait)
|
|
165
|
-
}
|
|
166
357
|
if propagationWait > 0 {
|
|
167
358
|
wait = time.Duration(propagationWait) * time.Second
|
|
168
359
|
}
|
pylego/lego.so
CHANGED
|
Binary file
|
pylego/pylego.py
CHANGED
|
@@ -10,6 +10,23 @@ so_file = here / ("lego.so")
|
|
|
10
10
|
library = ctypes.cdll.LoadLibrary(so_file)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
@dataclass
|
|
14
|
+
class Identifier:
|
|
15
|
+
"""ACME identifier (domain or IP)."""
|
|
16
|
+
|
|
17
|
+
type: str # "dns" or "ip"
|
|
18
|
+
value: str # Domain name or IP address
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Subproblem:
|
|
23
|
+
"""ACME subproblem details."""
|
|
24
|
+
|
|
25
|
+
type: str # Error type (e.g., "unauthorized", "dns")
|
|
26
|
+
detail: str # Human-readable message
|
|
27
|
+
identifier: Identifier # The identifier that caused this subproblem
|
|
28
|
+
|
|
29
|
+
|
|
13
30
|
@dataclass
|
|
14
31
|
class Metadata:
|
|
15
32
|
"""Extra information returned by the ACME server."""
|
|
@@ -31,7 +48,39 @@ class LEGOResponse:
|
|
|
31
48
|
|
|
32
49
|
|
|
33
50
|
class LEGOError(Exception):
|
|
34
|
-
"""
|
|
51
|
+
"""Unified exception for all errors returned by the lego invocation.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
type: source of the error. "acme" when coming from the ACME server, otherwise "lego".
|
|
55
|
+
code: error code/category. For ACME, this is derived from the ACME problem type; otherwise, it's set by lego.
|
|
56
|
+
status: HTTP status code for ACME errors, None otherwise.
|
|
57
|
+
detail: human-readable description of the error.
|
|
58
|
+
acme_type: full ACME problem type (URN), present only for ACME errors.
|
|
59
|
+
subproblems: list of Subproblem objects with detailed error information.
|
|
60
|
+
info: dictionary with the raw error information returned by the underlying call.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
detail: str,
|
|
66
|
+
*,
|
|
67
|
+
type: str = "lego",
|
|
68
|
+
code: str = "",
|
|
69
|
+
status: int | None = None,
|
|
70
|
+
acme_type: str = "",
|
|
71
|
+
subproblems: list[Subproblem] | None = None,
|
|
72
|
+
info: dict | None = None,
|
|
73
|
+
):
|
|
74
|
+
# Include code in exception message for better error display
|
|
75
|
+
message = f"[{code}] {detail}" if code else detail
|
|
76
|
+
super().__init__(message)
|
|
77
|
+
self.type = type
|
|
78
|
+
self.code = code
|
|
79
|
+
self.status = status
|
|
80
|
+
self.detail = detail
|
|
81
|
+
self.acme_type = acme_type
|
|
82
|
+
self.subproblems = subproblems or []
|
|
83
|
+
self.info = info or {}
|
|
35
84
|
|
|
36
85
|
|
|
37
86
|
def run_lego_command(
|
|
@@ -77,7 +126,42 @@ def run_lego_command(
|
|
|
77
126
|
"utf-8",
|
|
78
127
|
)
|
|
79
128
|
result: bytes = library.RunLegoCommand(message)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
129
|
+
result_str = result.decode("utf-8")
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
result_dict = json.loads(result_str)
|
|
133
|
+
except json.JSONDecodeError as e:
|
|
134
|
+
raise LEGOError(f"Failed to parse response: {result_str}") from e
|
|
135
|
+
|
|
136
|
+
if not result_dict.get("success", False):
|
|
137
|
+
error_info: dict = result_dict.get("error", {})
|
|
138
|
+
err_source = error_info.get("type", "lego")
|
|
139
|
+
detail = error_info.get("detail", "Unknown error occurred")
|
|
140
|
+
|
|
141
|
+
subproblems = []
|
|
142
|
+
for sub_dict in error_info.get("subproblems", []):
|
|
143
|
+
identifier_dict = sub_dict.get("identifier", {})
|
|
144
|
+
subproblems.append(
|
|
145
|
+
Subproblem(
|
|
146
|
+
type=sub_dict.get("type", ""),
|
|
147
|
+
detail=sub_dict.get("detail", ""),
|
|
148
|
+
identifier=Identifier(
|
|
149
|
+
type=identifier_dict.get("type", ""),
|
|
150
|
+
value=identifier_dict.get("value", ""),
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
info = dict(error_info)
|
|
156
|
+
raise LEGOError(
|
|
157
|
+
detail,
|
|
158
|
+
type="acme" if err_source == "acme" else "lego",
|
|
159
|
+
code=error_info.get("code", ""),
|
|
160
|
+
status=error_info.get("status"),
|
|
161
|
+
acme_type=error_info.get("acme_type", "") if err_source == "acme" else "",
|
|
162
|
+
subproblems=subproblems,
|
|
163
|
+
info=info,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
data = result_dict.get("data", {})
|
|
167
|
+
return LEGOResponse(**{**data, "metadata": Metadata(**data.get("metadata", {}))})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pylego
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.34
|
|
4
4
|
Summary: A python wrapper package for the lego application written in Golang
|
|
5
5
|
Author-email: Canonical <telco-engineers@lists.canonical.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/canonical/pylego
|
|
@@ -63,6 +63,29 @@ On top of the environment variables that LEGO supports, we have some extra ones
|
|
|
63
63
|
| `TLSALPN01_IFACE` | Interface for the TLS-ALPN-01 challenge (when `plugin=tls`). Any interface by default. |
|
|
64
64
|
| `TLSALPN01_PORT` | Port for the TLS-ALPN-01 challenge (when `plugin=tls`). 443 by default. |
|
|
65
65
|
|
|
66
|
+
## Error Handling
|
|
67
|
+
|
|
68
|
+
All errors raised by `run_lego_command()` are `LEGOError` exceptions with structured information:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from pylego import run_lego_command, LEGOError
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
result = run_lego_command(...)
|
|
75
|
+
except LEGOError as e:
|
|
76
|
+
print(f"Error: {e}") # Includes error code in message
|
|
77
|
+
print(f"Type: {e.type}") # "acme" (server) or "lego" (client)
|
|
78
|
+
print(f"Code: {e.code}") # e.g., "invalid_csr", "dns_provider_failed"
|
|
79
|
+
print(f"Detail: {e.detail}") # Human-readable message
|
|
80
|
+
|
|
81
|
+
# ACME-specific fields
|
|
82
|
+
if e.type == "acme":
|
|
83
|
+
print(f"Status: {e.status}") # HTTP status code
|
|
84
|
+
print(f"Subproblems: {e.subproblems}") # Validation details per domain
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Common error codes: `invalid_csr`, `invalid_private_key`, `dns_provider_failed`, `network_error`, `certificate_obtain_failed`. ACME errors include codes like `unauthorized`, `rateLimited`, `dns`.
|
|
88
|
+
|
|
66
89
|
## How does it work?
|
|
67
90
|
|
|
68
91
|
Golang supports building a shared c library from its CLI build tool. We import and use the LEGO application from GoLang, and provide a stub with C bindings so that the shared C binary we produce exposes a C API for other programs to import and utilize. pylego then uses the [ctypes](https://docs.python.org/3/library/ctypes.html) standard library in python to load this binary, and make calls to its methods.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pylego/__init__.py,sha256=7rcUcQcOWsOLxTOEXF2ASkwm_7eED1UIXzxdlgKPr5c,82
|
|
2
|
+
pylego/go.mod,sha256=qLNIZo1UcJ9msJWbn7CYkfi8rsKi29FfI0tjnw1GA4E,11485
|
|
3
|
+
pylego/go.sum,sha256=o_rnHFfbUwYJGixlas9y-yLlNgkBQWuWfRVTN8Xinyc,159459
|
|
4
|
+
pylego/lego.go,sha256=zauvVfyFVfH_4Z1lAiTAYRt-tU-0Z4_xmVjKhkP7uu8,11180
|
|
5
|
+
pylego/lego.so,sha256=wpSLjV_V1S5ql25Smea_jUYGEQ9jow1XHZ7lgeQWFYw,87331720
|
|
6
|
+
pylego/pylego.py,sha256=556axo5EwOUGG5ZtsHgfFog-CTkJtOsavdTGLgEAu6k,5522
|
|
7
|
+
pylego-0.1.34.dist-info/licenses/LICENSE,sha256=aklz9Y8CIpFsN61U4jHlJYp4W_8HoDpY-tINlDcdSZY,10934
|
|
8
|
+
pylego-0.1.34.dist-info/METADATA,sha256=CWz5i5KWYzGdEAxVUlJk7AUaNM71y1TwAaTEWszxpds,6703
|
|
9
|
+
pylego-0.1.34.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
10
|
+
pylego-0.1.34.dist-info/top_level.txt,sha256=pSOYv55_w90qy3xOvqz_ysSz-X-XRTb-jMpiOyLNnNs,7
|
|
11
|
+
pylego-0.1.34.dist-info/RECORD,,
|
pylego-0.1.32.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
pylego/__init__.py,sha256=7rcUcQcOWsOLxTOEXF2ASkwm_7eED1UIXzxdlgKPr5c,82
|
|
2
|
-
pylego/go.mod,sha256=2zddNtfcY9OT_qrCABQwRB06dvL0QF9kMA1E6lmvOlM,12467
|
|
3
|
-
pylego/go.sum,sha256=-Gl6lIAbKb0cjJHr4dVvA4_XGBSHONqL0lKRGHUoYn0,187420
|
|
4
|
-
pylego/lego.go,sha256=Fw9klKd_4pQV6SKn7O2WXLs-M_ta_l5nxZhluov0EeU,6509
|
|
5
|
-
pylego/lego.so,sha256=iY76dyNQa-lhximqoLrdTJPJ7ATqfbo_z1xncZEbprM,87362384
|
|
6
|
-
pylego/pylego.py,sha256=HgNEnKndDHskz5SoB3tOdSDNyd73E9X57Y3Ksvfog1Q,2609
|
|
7
|
-
pylego-0.1.32.dist-info/licenses/LICENSE,sha256=aklz9Y8CIpFsN61U4jHlJYp4W_8HoDpY-tINlDcdSZY,10934
|
|
8
|
-
pylego-0.1.32.dist-info/METADATA,sha256=hX8nkvZacF4-eZZk5Y_2TGxKWZ_ZJgNywHy6c3rtmXM,5776
|
|
9
|
-
pylego-0.1.32.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
-
pylego-0.1.32.dist-info/top_level.txt,sha256=pSOYv55_w90qy3xOvqz_ysSz-X-XRTb-jMpiOyLNnNs,7
|
|
11
|
-
pylego-0.1.32.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|