{"id":37148169,"url":"https://github.com/bugfan/acme","last_synced_at":"2026-01-14T17:29:25.452Z","repository":{"id":57623004,"uuid":"224110184","full_name":"bugfan/acme","owner":"bugfan","description":"acme for golang,use as library (http01,tls01)","archived":false,"fork":false,"pushed_at":"2022-06-28T08:11:46.000Z","size":164,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-20T12:46:17.677Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bugfan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-26T05:31:38.000Z","updated_at":"2022-01-25T12:25:02.000Z","dependencies_parsed_at":"2022-08-26T23:51:36.759Z","dependency_job_id":null,"html_url":"https://github.com/bugfan/acme","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/bugfan/acme","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugfan%2Facme","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugfan%2Facme/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugfan%2Facme/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugfan%2Facme/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bugfan","download_url":"https://codeload.github.com/bugfan/acme/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bugfan%2Facme/sbom","scorecard":{"id":257419,"data":{"date":"2025-08-11","repo":{"name":"github.com/bugfan/acme","commit":"0b3c6541ecfc46b02b03bbce2f49ad1397283c7a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"20 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2022-0968 / GHSA-gwc9-m7rh-j2ww","Warn: Project is vulnerable to: GO-2021-0356 / GHSA-8c26-wmh5-6g9v","Warn: Project is vulnerable to: GO-2024-2961","Warn: Project is vulnerable to: GO-2023-2402 / GHSA-45x7-px36-x8w8","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77","Warn: Project is vulnerable to: GO-2022-0236 / GHSA-h86h-8ppg-mxmh","Warn: Project is vulnerable to: GO-2021-0238 / GHSA-83g2-8m93-v3w7","Warn: Project is vulnerable to: GO-2022-0288","Warn: Project is vulnerable to: GO-2022-0969 / GHSA-69cg-p879-7622","Warn: Project is vulnerable to: GO-2022-1144 / GHSA-xrjj-mj9h-534m","Warn: Project is vulnerable to: GO-2023-1571 / GHSA-vvpx-j8f3-3w6h","Warn: Project is vulnerable to: GO-2023-1988 / GHSA-2wrh-6pvc-2jm9","Warn: Project is vulnerable to: GO-2023-2102 / GHSA-4374-p667-p6c8","Warn: Project is vulnerable to: GHSA-qppj-fm5r-hxr3","Warn: Project is vulnerable to: GO-2024-2687 / GHSA-4v7x-pqxf-cx7m","Warn: Project is vulnerable to: GO-2024-3333","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw","Warn: Project is vulnerable to: GO-2024-2631 / GHSA-c5q2-7r4c-mv6g"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T09:59:45.269Z","repository_id":57623004,"created_at":"2025-08-17T09:59:45.269Z","updated_at":"2025-08-17T09:59:45.269Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28427995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T16:38:47.836Z","status":"ssl_error","status_checked_at":"2026-01-14T16:34:59.695Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-14T17:29:24.719Z","updated_at":"2026-01-14T17:29:25.441Z","avatar_url":"https://github.com/bugfan.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## acme (use as library)\n- [ ] dns\n- [x] http01\n- [x] tlsalpn1\n\n\u003cbr\u003e\n\n## ✨ 简介\n\nHTTP-01：通过HTTP访问服务器80端口的/.well-known/acme-challenge验证。\nDNS-01：在DNS中添加_acme-challenge开头的TXT记录，这种方式因为能签发通配符证书（Wildcard）而被大范围使用。\nTLS-SNI-01、TLS-ALPN-01：通过TLS的方式对443端口访问进行验证。(TLS-ALPN-01支持的客户端非常少,TLS-SNI因为漏洞被遗弃)\n\n\u003cbr\u003e\n\n- tls\n```\n占用443端口，需要从从连接上拿到临时证书，然后监听，从这个tls上获取证书；或者通过获取现有证书从tls连接上验证并获取新证书\n```\n- http \n```\n占用80，交互只涉及到http固定目录，设随机码校验，校验通过，可以获取证书；也可以在nginx配置验证路由，在golang里面obtain证书\nserver {\n        listen  80 default_server;\n        root /usr/local/nginx/html;\n        location / {\n                return 404;\n        }\n        location ^~ /.well-known/acme-challenge/ {\n                default_type \"text/plain\";\n\n        }\n        location = /.well-known/acme-challenge/ {\n                return 404;\n        }\n```\n- dns\n```\n不占端口，需要知道dns厂商，需要在dns服务器做txt记录，但是可以获取泛域名证书\n```\n\n\u003cbr\u003e\n\n## 🦊 如何使用？👇是一些例子\n\n- http01方式\n```\n// demo1\nimport \"github.com/bugfan/acme\"\n\ntype H struct {}\nfunc (h *H) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tfmt.Println(\"your http.handler\", r.URL.String())\n    // todo something\n\n}\nfunc main(){\n    h := \u0026H{}\n    p := acme.NewHTTP01Provider(h)\n\ta, err := acme.NewACME(\"youremail\")\n\tif err != nil {\n\t\tfmt.Println(\"new error:\", err)\n\t\treturn\n\t}\n\ta.SetHTTP01Provider(p)\n    cert, err := a.Obtain(\"app.xxx.cn\")\n    if err != nil {\n        fmt.Println(\"obtain result:\", err)\n        return\n    }\n    fmt.Println(\"obtain result:\", cert.Key, cert.Cert, cert.Intermediate)\n}\n```\n```\n// demo2\nimport \"github.com/bugfan/acme\"\n\ntype H struct {}\nfunc (h *H) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tfmt.Println(\"your http.handler\", r.URL.String())\n    // todo something\n\n}\nfunc main(){\n    h := \u0026H{}\n    p := acme.NewHTTP01Provider(h)\n\ta, err := acme.NewACME(\"youremail\")\n\tif err != nil {\n\t\tfmt.Println(\"new error:\", err)\n\t\treturn\n\t}\n\t// a.SetHTTP01Provider(p) // not set\n    cert, err := a.Obtain(\"app.xxx.cn\")\n    if err != nil {\n        fmt.Println(\"obtain result:\", err)\n        return\n    }\n    fmt.Println(\"obtain result:\", cert.Key, cert.Cert, cert.Intermediate)\n\thttp.ListenAndServe(\":80\", nil) // if not set,should you to listen at '80'\n}\n```\n\n- tlsalpn1方式\n```\n// demo1 你的服务没有占用443（显然这种情况比较少）\nimport \"github.com/bugfan/acme\"\nfunc main(){\n   a, err := acme.NewACME(\"your@qq.com\")\n\tif err != nil {\n\t\tfmt.Println(\"new error:\", err)\n\t\treturn\n\t}\n\ttp := acme.NewDefaultTLSALPN01Provider()\n\ta.SetTLSALPN01Provider(tp)\n\tcert, err := a.Obtain(\"app.xxx.cn\")\n\tif err != nil {\n\t\tfmt.Println(\"obtain result:\", err)\n\t\treturn\n\t}\n\tfmt.Println(\"obtain result:\", cert.Key, cert.Cert, cert.Intermediate)\n}\n```\n```\n// demo2 你的服务占用了443\nimport \"github.com/bugfan/acme\"\nvar myacme acme.ACME\nfunc init() {\n\tvar err error\n\tmyacme, err = acme.NewACME(\"89898989@qq.com\")\n\tif err != nil {\n\t\tfmt.Println(\"new acme error:\", err)\n\t}\n}\nfunc main() {\n\t// ...\n\n\tgo func() {\n\t\tgetYourHTTPSServer().ListenAndServeTLS(\"your-cert-filt-path\", \"your-key-file-path\")\n\t}()\n\n\tgo func() {\n\t\ttime.Sleep(5e9) // or if when you need obtain\n\t\tcert, err := myacme.Obtain(\"app.xxx.cn\")\n\t\tif err != nil {\n\t\t\tfmt.Println(\"obtain result:\", err)\n\t\t\treturn\n\t\t}\n\t\tfmt.Println(\"obtain result:\", cert.Key, cert.Cert, cert.Intermediate)\n\t}()\n\n    // ...\n\t\u003c-chan int(nil)\n}\nfunc getYourHTTPSServer() *http.Server {\n\ttlsConf := \u0026tls.Config{}\n\thandler := \u0026H{}\n    /*\n    * one of the following ways is ok\n    */\n\t// demo1 Certificates()\n\t// certProvider := handler\n\n\t//demo2 GetCertificate(*tls.ClientHelloInfo)\n\tcertProvider := handler\n\n\t//demo3 GetConfigForClient(*tls.ClientHelloInfo)\n\t// certProvider := handler\n\n\ttc, err := acme.NewACMETLSConfig(tlsConf, certProvider)\n\tif err != nil {\n\t\tfmt.Println(\"NewACMETLSConfig error:\", err)\n\t\tpanic(err) // or tc=tlsConf\n\t}\n\treturn \u0026http.Server{\n\t\tHandler:   handler,\n\t\tTLSConfig: tc,\n\t\tAddr:      \":443\",\n\t}\n}\ntype H struct{}\nfunc (h *H) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tfmt.Println(\"your http.handler\", r.URL.String())\n\t// todo something\n\n}\nfunc (h *H) Certificates() []*tls.Certificate {\n\tcerts := make([]*tls.Certificate, 0)\n\tcerts = append(certs, h.Cert())\n\treturn certs\n}\nfunc (h *H) Cert() *tls.Certificate {\n\t// need read from your cert file\n\tyourcert, _ := tls.X509KeyPair([]byte(fmt.Sprintf(\"%s\\n%s\", \"your cert data\", \"your intermediate data\")), []byte(\"your key\"))\n\treturn \u0026yourcert\n}\nvar ServerIP string = \"212.45.32.55\" // your server ip\nfunc (h *H) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {\n\tif hello.ServerName == ServerIP {\n\t\treturn h.Cert(), nil\n\t}\n\treturn nil, nil\n}\n```\n\n\u003cbr\u003e\n\n## 🐛 签发免费证书常见问题\n- http 没有备案被云厂商拦截了\n ```\n[app.xxx.cn] acme: error: 403 :: urn:ietf:params:acme:error:unauthorized :: During secondary validation: Invalid response from https://dnspod.qcloud.com/static/webblock.html?d=app.xxx.cn [203.205.224.59]: \"\u003c!DOCTYPE html\u003e\\n\u003chtml\u003e\\n\\t\u003chead\u003e\\n\\t\\t\u003cmeta charset=\\\"utf-8\\\" /\u003e\\n\\t\\t\u003cmeta http-equiv=\\\"X-UA-Compatible\\\" content=\\\"IE=edge,chrome=1\\\" /\u003e\\n\\t\\t\u003c\", url: \n ```\n\n- tls 不是tls的监听在443\n ```\nroot@VM-0-10-ubuntu:/home# ./acem \n2021/03/10 18:20:31 [INFO] acme: Registering account for 917719033@qq.com\n2021/03/10 18:20:32 [INFO] [www.xxxx.cn] acme: Obtaining bundled SAN certificate\n2021/03/10 18:20:32 [INFO] [www.xxxx.cn] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/11452689397\n2021/03/10 18:20:32 [INFO] [www.xxxx.cn] acme: use tls-alpn-01 solver\n2021/03/10 18:20:32 [INFO] [www.xxxx.cn] acme: Trying to solve TLS-ALPN-01\n2021/03/10 18:20:39 [INFO] Unable to deactivate the authorization: https://acme-v02.api.letsencrypt.org/acme/authz-v3/11452689397\n2021/03/10 18:20:39 acme: Error -\u003e One or more domains had a problem:\n[www.xxxx.cn] acme: error: 400 :: urn:ietf:params:acme:error:malformed :: Server only speaks HTTP, not TLS, url:\n ```\n\n- tls 443没起\n ```\n // 443没监听\n2021/03/20 22:32:57 Unable to deactivate the authorization: https://acme-v02.api.letsencrypt.org/acme/authz-v3/11705545896\nobtain result2: error: one or more domains had a problem:\n[www.xxxx.cn] acme: error: 400 :: urn:ietf:params:acme:error:connection :: Connection refused\n ```\n\n- tls 这是tls层的tls.config配置缺东西\n ```\n// tls server tlsconfig配置缺东西\n%w tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config\n ```\n\n- rate 这是签发次数太多了\n ```\n2021/03/21 19:12:14 [app.xxx.cn] acme: Obtaining bundled SAN certificate\nobtain result2: acme: error: 429 :: POST :: https://acme-v02.api.letsencrypt.org/acme/new-order :: urn:ietf:params:acme:error:rateLimited :: Error creating new order :: too many certificates already issued for exact set of domains: app.xxx.cn: see https://letsencrypt.org/docs/rate-limits/\n ```\n\n- 解析不对\n ```\nwww.xxx.cn 签发失败: get directory at 'https://acme-v02.api.letsencrypt.org/directory': Get \"https://acme-v02.api.letsencrypt.org/directory\": dial tcp: lookup acme-v02.api.letsencrypt.org on 127.0.0.11:53: server misbehaving\n\n解析不对，进入app这个容器（或者主机）内部，执行 echo '172.65.32.248 acme-v02.api.letsencrypt.org' \u003e /etc/hosts\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbugfan%2Facme","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbugfan%2Facme","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbugfan%2Facme/lists"}