-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 047a359
Showing
14 changed files
with
1,036 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
.idea/ | ||
|
||
# Binaries for programs and plugins | ||
*.exe | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, build with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 | ||
.glide/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
FROM golang:1.9 as builder | ||
|
||
WORKDIR /go/src/github.com/project0/certjunkie/ | ||
|
||
COPY . . | ||
RUN go get -v | ||
|
||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o certjunkie . | ||
|
||
FROM alpine:latest | ||
RUN apk --no-cache add ca-certificates | ||
|
||
WORKDIR /root/ | ||
|
||
COPY --from=builder /go/src/github.com/project0/certjunkie/certjunkie . | ||
|
||
ENTRYPOINT ["./certjunkie"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2017 Richard Hillmann | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# CertJunkie | ||
This project is inspired by [acme-dns](https://github.com/joohoi/acme-dns). While acme-dns is awesome to use with other acme clients, it lacks of capabilities of shared certs and anonymous usage. | ||
|
||
I want to have a simple http server to create, challenge and receive my (lets encrypt) certs from an central point. | ||
As it is intended to be used within an private and closed context, optional authentication and secured connection is currently not focused (fee free to create PR). | ||
|
||
## Usage | ||
|
||
``` | ||
--dns.domain string The NS domain name of this server (default "ns.local") | ||
--dns.listen string Bind on this port to run the DNS server on (tcp and udp) (default ":53") | ||
--dns.zone string The zone we are using to provide the txt records for challenge (default "acme.local") | ||
--email string Registration email for the ACME server | ||
--listen string Bind on this port to run the API server on (default ":80") | ||
--provider string DNS challenge provider name (default "dnscname") | ||
--server string ACME Directory Resource URI (default "https://acme-v01.api.letsencrypt.org/directory") | ||
--storage string Storage driver to use, currently only local is supported (default "local") | ||
--storage.local string Path to store the certs and account data for local storage driver (default "$HOME/.certjunkie") | ||
``` | ||
|
||
For combatible dns provdider look at https://github.com/xenolf/lego/tree/master/providers/dns | ||
|
||
### Docker | ||
[Image DockerHub](https://hub.docker.com/project0de/certjunkie) | ||
|
||
```bash | ||
docker run -ti -p 80:80 -p 53:53 -p 53:53/udp \ | ||
-v $(pwd)/certjunkie:/storage project0de/certjunkie \ | ||
--storage.local /storage --email [email protected] --dns.zone certjunkie.domain.com --dns.domain thisserver.domain.com | ||
``` | ||
|
||
### Client example with curl | ||
```bash | ||
curl http://localhost:8080/cert/my.domain.de/cert -Fo my.domain.de.crt && \ | ||
curl http://localhost:8080/cert/my.domain.de/key -Fo my.domain.de.key && \ | ||
curl http://localhost:8080/cert/my.domain.de/ca -Fo my.domain.de.ca | ||
``` | ||
|
||
## `dnscname` DNS redirect with CNAME | ||
This is actually `$challengeDomain.$dnsDomain.`. | ||
Ensure the NS record is set to this server | ||
|
||
### Example | ||
Asume starting with `certjunkie --dns.domain certjunkiens.example.com --dns.zone certjunkie.example.com --email [email protected]` | ||
|
||
1. Delegate a subdomain to the server running certbot on your remote hosted DNS `example.com`: | ||
``` | ||
certjunkiens A 1.1.1.1 300 # this should be A/AAAA record | ||
certunkie NS certjunkiens.example.com # delegate zone to our built in nameserver | ||
``` | ||
|
||
2. Setup certjunkie to start with his new authorative domain `certjunkie.example.com` | ||
|
||
3. Forward the acme txt record for domains you would like to automate challenge: | ||
``` | ||
_acme-challenge.yourdomain.com CNAME yourdomain.com.certjunkie.example.com | ||
_acme-challenge.www.yourdomain.com CNAME www.yourdomain.com.certjunkie.example.com | ||
_acme-challenge.service.cloud.yourdomain.com CNAME service.cloud.yourdomain.com.certjunkie.example.com | ||
``` | ||
|
||
## API | ||
* `domain`: Get an cert which matches this domain. | ||
|
||
### GET /cert/{domain} | ||
|
||
Get JSON of an cert with CA and key | ||
If the cert does not exist (or is not valid anymore) it will request a new one (sync). | ||
|
||
#### Optional query parameters | ||
* `san`: Comma separated list of subject alternative names the cert must have. | ||
* `onlycn`: Get only a cert which matches the CommonName | ||
* `valid`: How long needs the cert to be valid in days before requesting a new one. Defaults to 30 | ||
|
||
### GET /cert/{domain}/cert | ||
Retrieve only the certificate pem encoded. | ||
|
||
### GET /cert/{domain}/ca | ||
Retrieve only the Issuer Certificate (CA) pem encoded. | ||
|
||
### GET /cert/{domain}/bundle | ||
Retrieve bundled cert with ca pem encoded. | ||
|
||
### GET /cert/{domain}/key | ||
Retrieve the private key pem encoded. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package api | ||
|
||
import ( | ||
"log" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/gorilla/handlers" | ||
"github.com/gorilla/mux" | ||
|
||
"github.com/project0/certjunkie/certstore" | ||
) | ||
|
||
func NewApiServer(listen string, store *certstore.CertStore) { | ||
|
||
apiCert := apiCert{ | ||
store: store, | ||
} | ||
|
||
r := mux.NewRouter() | ||
r.HandleFunc("/cert/{domain}", apiCert.getJson).Methods(http.MethodGet) | ||
r.HandleFunc("/cert/{domain}/cert", apiCert.getCert).Methods(http.MethodGet) | ||
r.HandleFunc("/cert/{domain}/ca", apiCert.getCA).Methods(http.MethodGet) | ||
r.HandleFunc("/cert/{domain}/key", apiCert.getKey).Methods(http.MethodGet) | ||
r.HandleFunc("/cert/{domain}/bundle", apiCert.getBundle).Methods(http.MethodGet) | ||
|
||
log.Printf("Start listening http server on %s", listen) | ||
go func() { | ||
err := http.ListenAndServe(listen, handlers.LoggingHandler(os.Stdout, r)) | ||
if err != nil { | ||
log.Fatalf("Failed to setup the http server: %s\n", err.Error()) | ||
} | ||
}() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package api | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/gorilla/mux" | ||
|
||
"github.com/project0/certjunkie/certstore" | ||
) | ||
|
||
type apiCert struct { | ||
store *certstore.CertStore | ||
} | ||
|
||
// certRequest obtains a cert from the certstore | ||
func (a *apiCert) certRequest(w http.ResponseWriter, r *http.Request) *certstore.CertificateResource { | ||
var err error | ||
vars := mux.Vars(r) | ||
if vars["domain"] == "" { | ||
http.Error(w, fmt.Sprintf("Domain name %s is not valid", vars["domain"]), http.StatusBadRequest) | ||
return nil | ||
} | ||
query := r.URL.Query() | ||
cr := certstore.CertRequest{ | ||
Domain: vars["domain"], | ||
ValidDays: 30, | ||
} | ||
|
||
if query.Get("onlycn") != "" { | ||
cr.DomainIsCn = true | ||
} | ||
|
||
if query.Get("valid") != "" { | ||
cr.ValidDays, err = strconv.Atoi(query.Get("valid")) | ||
if err != nil { | ||
http.Error(w, fmt.Sprintf("Invalid value for parameter valid: %v", err), http.StatusBadRequest) | ||
return nil | ||
} | ||
} | ||
|
||
if query.Get("san") != "" { | ||
cr.San = strings.Split(query.Get("san"), ",") | ||
} | ||
|
||
cert, err := a.store.GetCertificate(&cr) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return nil | ||
} | ||
|
||
return cert | ||
} | ||
|
||
func (a *apiCert) getJson(w http.ResponseWriter, r *http.Request) { | ||
cert := a.certRequest(w, r) | ||
if cert == nil { | ||
return | ||
} | ||
w.Header().Set("Content-Type", "application/json") | ||
w.WriteHeader(http.StatusOK) | ||
json.NewEncoder(w).Encode(cert) | ||
} | ||
|
||
func (a *apiCert) getCert(w http.ResponseWriter, r *http.Request) { | ||
cert := a.certRequest(w, r) | ||
if cert == nil { | ||
return | ||
} | ||
w.WriteHeader(http.StatusOK) | ||
w.Write(cert.Certificate) | ||
} | ||
|
||
func (a *apiCert) getCA(w http.ResponseWriter, r *http.Request) { | ||
cert := a.certRequest(w, r) | ||
if cert == nil { | ||
return | ||
} | ||
w.WriteHeader(http.StatusOK) | ||
w.Write(cert.IssuerCertificate) | ||
} | ||
|
||
func (a *apiCert) getKey(w http.ResponseWriter, r *http.Request) { | ||
cert := a.certRequest(w, r) | ||
if cert == nil { | ||
return | ||
} | ||
w.WriteHeader(http.StatusOK) | ||
w.Write(cert.PrivateKey) | ||
} | ||
|
||
func (a *apiCert) getBundle(w http.ResponseWriter, r *http.Request) { | ||
cert := a.certRequest(w, r) | ||
if cert == nil { | ||
return | ||
} | ||
w.WriteHeader(http.StatusOK) | ||
w.Write(append(cert.Certificate, cert.IssuerCertificate...)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package certstore | ||
|
||
import ( | ||
"crypto/x509" | ||
"encoding/pem" | ||
) | ||
|
||
// CertificateResource represent everything from our cert | ||
type CertificateResource struct { | ||
Domain string `json:"domain"` | ||
PrivateKey []byte `json:"key"` | ||
Certificate []byte `json:"certificate"` | ||
IssuerCertificate []byte `json:"issuer"` | ||
} | ||
|
||
func (c *CertificateResource) parseCert() (*x509.Certificate, error) { | ||
block, _ := pem.Decode(c.Certificate) | ||
return x509.ParseCertificate(block.Bytes) | ||
} |
Oops, something went wrong.