chore: Format

This commit is contained in:
PapaTutuWawa 2024-01-01 00:38:39 +01:00
parent 1154eff9ae
commit 85bd71cff3
7 changed files with 97 additions and 100 deletions

View File

@ -5,8 +5,8 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"encoding/json"
"encoding/base64" "encoding/base64"
"encoding/json"
"io/ioutil" "io/ioutil"
"github.com/go-acme/lego/v4/acme" "github.com/go-acme/lego/v4/acme"
@ -16,9 +16,9 @@ import (
) )
type AcmeAccount struct { type AcmeAccount struct {
Email string Email string
Registration *registration.Resource Registration *registration.Resource
Key crypto.PrivateKey Key crypto.PrivateKey
} }
func (a *AcmeAccount) GetEmail() string { func (a *AcmeAccount) GetEmail() string {
@ -36,7 +36,7 @@ func (a *AcmeAccount) GetRegistration() *registration.Resource {
func (a *AcmeAccount) FlushToDisk(storage string) { func (a *AcmeAccount) FlushToDisk(storage string) {
data := map[string]interface{}{ data := map[string]interface{}{
"email": a.Email, "email": a.Email,
"reg": a.Registration, "reg": a.Registration,
"private_key_encoded": base64.StdEncoding.EncodeToString( "private_key_encoded": base64.StdEncoding.EncodeToString(
certcrypto.PEMEncode(a.Key), certcrypto.PEMEncode(a.Key),
), ),
@ -61,22 +61,21 @@ func ClientFromFile(storage, acmeServer string) (*lego.Client, error) {
contacts = append(contacts, v.(string)) contacts = append(contacts, v.(string))
} }
registration := registration.Resource{ registration := registration.Resource{
URI: reg["uri"].(string), URI: reg["uri"].(string),
Body: acme.Account{ Body: acme.Account{
Status: accountRaw["status"].(string), Status: accountRaw["status"].(string),
Contact: contacts, Contact: contacts,
TermsOfServiceAgreed: true, TermsOfServiceAgreed: true,
//Orders: accountRaw["orders"].(string), //Orders: accountRaw["orders"].(string),
}, },
} }
pkEncoded, err := base64.StdEncoding.DecodeString(data["private_key_encoded"].(string)) pkEncoded, err := base64.StdEncoding.DecodeString(data["private_key_encoded"].(string))
pk, err := certcrypto.ParsePEMPrivateKey(pkEncoded) pk, err := certcrypto.ParsePEMPrivateKey(pkEncoded)
account := AcmeAccount{ account := AcmeAccount{
Email: data["email"].(string), Email: data["email"].(string),
Key: pk.(*ecdsa.PrivateKey), Key: pk.(*ecdsa.PrivateKey),
Registration: &registration, Registration: &registration,
} }
config := lego.NewConfig(&account) config := lego.NewConfig(&account)
@ -99,7 +98,7 @@ func GenerateNewAccount(email, storage, acmeServer string) (*lego.Client, error)
account := AcmeAccount{ account := AcmeAccount{
Email: email, Email: email,
Key: privateKey, Key: privateKey,
} }
config := lego.NewConfig(&account) config := lego.NewConfig(&account)
config.CADirURL = acmeServer config.CADirURL = acmeServer
@ -109,7 +108,7 @@ func GenerateNewAccount(email, storage, acmeServer string) (*lego.Client, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Register it // Register it
req, err := client.Registration.Register( req, err := client.Registration.Register(
registration.RegisterOptions{ registration.RegisterOptions{
@ -121,6 +120,6 @@ func GenerateNewAccount(email, storage, acmeServer string) (*lego.Client, error)
} }
account.Registration = req account.Registration = req
account.FlushToDisk(storage) account.FlushToDisk(storage)
return client, nil return client, nil
} }

60
acme.go
View File

@ -31,7 +31,7 @@ const (
var ( var (
// Well-known -> Challenge solution // Well-known -> Challenge solution
runningChallenges = make(map[string]string) runningChallenges = make(map[string]string)
Certificates = CertificatesCache{ Certificates = CertificatesCache{
Certificates: make(map[string]CertificateWrapper), Certificates: make(map[string]CertificateWrapper),
} }
@ -63,30 +63,30 @@ func unlockDomain(domain string) {
} }
type CertificateWrapper struct { type CertificateWrapper struct {
TlsCertificate *tls.Certificate `json:"-"` TlsCertificate *tls.Certificate `json:"-"`
Domain string `json:"domain"` Domain string `json:"domain"`
NotAfter time.Time `json:"not_after"` NotAfter time.Time `json:"not_after"`
PrivateKeyEncoded string `json:"private_key"` PrivateKeyEncoded string `json:"private_key"`
Certificate []byte `json:"certificate"` Certificate []byte `json:"certificate"`
IssuerCertificate []byte `json:"issuer_certificate"` IssuerCertificate []byte `json:"issuer_certificate"`
CertificateUrl string `json:"certificate_url"` CertificateUrl string `json:"certificate_url"`
} }
func (c *CertificateWrapper) GetPrivateKey() *rsa.PrivateKey { func (c *CertificateWrapper) GetPrivateKey() *rsa.PrivateKey {
data, _ := base64.StdEncoding.DecodeString(c.PrivateKeyEncoded) data, _ := base64.StdEncoding.DecodeString(c.PrivateKeyEncoded)
pk, _ := certcrypto.ParsePEMPrivateKey(data); pk, _ := certcrypto.ParsePEMPrivateKey(data)
return pk.(*rsa.PrivateKey) return pk.(*rsa.PrivateKey)
} }
type CertificatesCache struct { type CertificatesCache struct {
FallbackCertificate *CertificateWrapper FallbackCertificate *CertificateWrapper
Certificates map[string]CertificateWrapper Certificates map[string]CertificateWrapper
} }
type CertificatesStore struct { type CertificatesStore struct {
FallbackCertificate CertificateWrapper `json:"fallback"` FallbackCertificate CertificateWrapper `json:"fallback"`
Certificates []CertificateWrapper `json:"certificates"` Certificates []CertificateWrapper `json:"certificates"`
} }
func (c *CertificatesCache) toStoreData() string { func (c *CertificatesCache) toStoreData() string {
@ -97,7 +97,7 @@ func (c *CertificatesCache) toStoreData() string {
result, err := json.Marshal(CertificatesStore{ result, err := json.Marshal(CertificatesStore{
FallbackCertificate: *c.FallbackCertificate, FallbackCertificate: *c.FallbackCertificate,
Certificates: certs, Certificates: certs,
}) })
if err != nil { if err != nil {
log.Errorf("Failed to Marshal cache: %v", err) log.Errorf("Failed to Marshal cache: %v", err)
@ -129,7 +129,7 @@ func CertificateFromStoreData(rawJson string) CertificatesCache {
certs[cert.Domain] = cert certs[cert.Domain] = cert
} }
cache.Certificates = certs cache.Certificates = certs
return cache return cache
} }
@ -160,17 +160,17 @@ func fallbackCert(pagesDomain string) (*CertificateWrapper, error) {
return nil, err return nil, err
} }
notAfter := time.Now().Add(time.Hour*24*7); notAfter := time.Now().Add(time.Hour * 24 * 7)
cert := x509.Certificate{ cert := x509.Certificate{
SerialNumber: big.NewInt(1), SerialNumber: big.NewInt(1),
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: pagesDomain, CommonName: pagesDomain,
Organization: []string{"Pages Server"}, Organization: []string{"Pages Server"},
}, },
NotAfter: notAfter, NotAfter: notAfter,
NotBefore: time.Now(), NotBefore: time.Now(),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true, BasicConstraintsValid: true,
} }
certBytes, err := x509.CreateCertificate( certBytes, err := x509.CreateCertificate(
@ -187,7 +187,7 @@ func fallbackCert(pagesDomain string) (*CertificateWrapper, error) {
out := &bytes.Buffer{} out := &bytes.Buffer{}
err = pem.Encode(out, &pem.Block{ err = pem.Encode(out, &pem.Block{
Bytes: certBytes, Bytes: certBytes,
Type: "CERTIFICATE", Type: "CERTIFICATE",
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -195,23 +195,23 @@ func fallbackCert(pagesDomain string) (*CertificateWrapper, error) {
outBytes := out.Bytes() outBytes := out.Bytes()
res := &certificate.Resource{ res := &certificate.Resource{
PrivateKey: certcrypto.PEMEncode(key), PrivateKey: certcrypto.PEMEncode(key),
Certificate: outBytes, Certificate: outBytes,
IssuerCertificate: outBytes, IssuerCertificate: outBytes,
Domain: pagesDomain, Domain: pagesDomain,
} }
tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &CertificateWrapper{ return &CertificateWrapper{
TlsCertificate: &tlsCertificate, TlsCertificate: &tlsCertificate,
Domain: pagesDomain, Domain: pagesDomain,
NotAfter: notAfter, NotAfter: notAfter,
PrivateKeyEncoded: base64.StdEncoding.EncodeToString(certcrypto.PEMEncode(key)), PrivateKeyEncoded: base64.StdEncoding.EncodeToString(certcrypto.PEMEncode(key)),
Certificate: outBytes, Certificate: outBytes,
IssuerCertificate: outBytes, IssuerCertificate: outBytes,
CertificateUrl: "localhost", CertificateUrl: "localhost",
}, nil }, nil
} }
@ -222,7 +222,7 @@ func isCertStillValid(cert CertificateWrapper) bool {
func makeTlsConfig(pagesDomain, path string, acmeClient *lego.Client) *tls.Config { func makeTlsConfig(pagesDomain, path string, acmeClient *lego.Client) *tls.Config {
return &tls.Config{ return &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
GetCertificate: func (info *tls.ClientHelloInfo) (*tls.Certificate, error) { GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
// Validate that we should even care about this domain // Validate that we should even care about this domain
if !strings.HasSuffix(info.ServerName, pagesDomain) { if !strings.HasSuffix(info.ServerName, pagesDomain) {
// Note: We do not check err here because err != nil // Note: We do not check err here because err != nil

View File

@ -13,7 +13,7 @@ import (
func ObtainNewCertificate(domains []string, path string, acmeClient *lego.Client) error { func ObtainNewCertificate(domains []string, path string, acmeClient *lego.Client) error {
req := certificate.ObtainRequest{ req := certificate.ObtainRequest{
Domains: domains, Domains: domains,
Bundle: true, Bundle: true,
} }
cert, err := acmeClient.Certificate.Obtain(req) cert, err := acmeClient.Certificate.Obtain(req)
if err != nil { if err != nil {
@ -27,13 +27,13 @@ func ObtainNewCertificate(domains []string, path string, acmeClient *lego.Client
wrapper := CertificateWrapper{ wrapper := CertificateWrapper{
TlsCertificate: &tlsCert, TlsCertificate: &tlsCert,
Domain: cert.Domain, Domain: cert.Domain,
//NotAfter: tlsCert.Leaf.NotAfter, //NotAfter: tlsCert.Leaf.NotAfter,
NotAfter: time.Now().Add(time.Hour * 24 * 60), NotAfter: time.Now().Add(time.Hour * 24 * 60),
PrivateKeyEncoded: base64.StdEncoding.EncodeToString(cert.PrivateKey), PrivateKeyEncoded: base64.StdEncoding.EncodeToString(cert.PrivateKey),
Certificate: cert.Certificate, Certificate: cert.Certificate,
IssuerCertificate: cert.IssuerCertificate, IssuerCertificate: cert.IssuerCertificate,
CertificateUrl: cert.CertURL, CertificateUrl: cert.CertURL,
} }
Certificates.Certificates[cert.Domain] = wrapper Certificates.Certificates[cert.Domain] = wrapper
FlushCertificateStoreToFile(path) FlushCertificateStoreToFile(path)

4
dns.go
View File

@ -14,8 +14,8 @@ const (
) )
var ( var (
cnameCache = cache.New(1 * time.Hour, 1 * time.Hour) cnameCache = cache.New(1*time.Hour, 1*time.Hour)
txtRepoCache = cache.New(1 * time.Hour, 1 * time.Hour) txtRepoCache = cache.New(1*time.Hour, 1*time.Hour)
) )
func lookupRepoTXT(domain string) (string, error) { func lookupRepoTXT(domain string) (string, error) {

73
main.go
View File

@ -2,18 +2,18 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"os"
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"os"
"strings" "strings"
"time" "time"
"code.gitea.io/sdk/gitea" "code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
"github.com/go-acme/lego/v4/challenge/http01" "github.com/go-acme/lego/v4/challenge/http01"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
) )
const ( const (
@ -35,7 +35,6 @@ func handleSubdomain(domain string, cname string, path, giteaUrl string, giteaCl
cname, cname,
path, path,
giteaClient, giteaClient,
) )
if err != nil { if err != nil {
log.Errorf("Failed to get repo: %s", err) log.Errorf("Failed to get repo: %s", err)
@ -50,7 +49,7 @@ func Handler(pagesDomain, giteaUrl string, giteaClient *gitea.Client) http.Handl
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Server", "rio") w.Header().Set("Server", "rio")
if strings.HasSuffix(req.Host, pagesDomain){ if strings.HasSuffix(req.Host, pagesDomain) {
if handleLetsEncryptChallenge(w, req) { if handleLetsEncryptChallenge(w, req) {
return return
} }
@ -59,7 +58,7 @@ func Handler(pagesDomain, giteaUrl string, giteaClient *gitea.Client) http.Handl
handleSubdomain(req.Host, "", req.URL.Path, giteaUrl, giteaClient, w) handleSubdomain(req.Host, "", req.URL.Path, giteaUrl, giteaClient, w)
return return
} }
cname, err := lookupCNAME(req.Host) cname, err := lookupCNAME(req.Host)
if err != nil { if err != nil {
log.Warningf("CNAME request failed, we don't handle %s", req.Host) log.Warningf("CNAME request failed, we don't handle %s", req.Host)
@ -177,68 +176,68 @@ func main() {
Action: runServer, Action: runServer,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "gitea-url", Name: "gitea-url",
Usage: "The (HTTPS) URL to the serving Gitea instance", Usage: "The (HTTPS) URL to the serving Gitea instance",
EnvVars: []string{"GITEA_URL"}, EnvVars: []string{"GITEA_URL"},
Required: true, Required: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "listen-host", Name: "listen-host",
Usage: "The host to listen on", Usage: "The host to listen on",
EnvVars: []string{"HOST"}, EnvVars: []string{"HOST"},
Value: "127.0.0.1", Value: "127.0.0.1",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "listen-port", Name: "listen-port",
Usage: "The port to listen on", Usage: "The port to listen on",
EnvVars: []string{"PORT"}, EnvVars: []string{"PORT"},
Value: "8888", Value: "8888",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "acme-host", Name: "acme-host",
Usage: "The host to bind to for ACME challenges", Usage: "The host to bind to for ACME challenges",
EnvVars: []string{"ACME_HOST"}, EnvVars: []string{"ACME_HOST"},
Value: "", Value: "",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "acme-port", Name: "acme-port",
Usage: "The port to listen on for ACME challenges", Usage: "The port to listen on for ACME challenges",
EnvVars: []string{"ACME_PORT"}, EnvVars: []string{"ACME_PORT"},
Value: "8889", Value: "8889",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "pages-domain", Name: "pages-domain",
Usage: "The domain on which the server is reachable", Usage: "The domain on which the server is reachable",
EnvVars: []string{"PAGES_DOMAIN"}, EnvVars: []string{"PAGES_DOMAIN"},
Required: true, Required: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "certs-file", Name: "certs-file",
Usage: "File to store certificates in", Usage: "File to store certificates in",
EnvVars: []string{"CERTS_FILE"}, EnvVars: []string{"CERTS_FILE"},
Value: "", Value: "",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "acme-file", Name: "acme-file",
Usage: "File to store ACME configuration in", Usage: "File to store ACME configuration in",
EnvVars: []string{"ACME_FILE"}, EnvVars: []string{"ACME_FILE"},
Value: "", Value: "",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "acme-email", Name: "acme-email",
Usage: "Email to use for an ACME account", Usage: "Email to use for an ACME account",
EnvVars: []string{"ACME_EMAIL"}, EnvVars: []string{"ACME_EMAIL"},
Value: "", Value: "",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "acme-server", Name: "acme-server",
Usage: "CA Directory to use", Usage: "CA Directory to use",
EnvVars: []string{"ACME_SERVER"}, EnvVars: []string{"ACME_SERVER"},
Value: "https://acme-staging-v02.api.letsencrypt.org/directory", Value: "https://acme-staging-v02.api.letsencrypt.org/directory",
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "acme-disable", Name: "acme-disable",
Usage: "Whether to disable automatic ACME certificates", Usage: "Whether to disable automatic ACME certificates",
EnvVars: []string{"ACME_DISABLE"}, EnvVars: []string{"ACME_DISABLE"},
}, },
}, },

View File

@ -13,12 +13,12 @@ import (
) )
var ( var (
pageCache = cache.New(6 * time.Hour, 1 * time.Hour) pageCache = cache.New(6*time.Hour, 1*time.Hour)
) )
type PageContentCache struct { type PageContentCache struct {
Content []byte Content []byte
mimeType string mimeType string
RequestedAt time.Time RequestedAt time.Time
} }
@ -98,7 +98,7 @@ func serveFile(username, reponame, path, giteaUrl string, w http.ResponseWriter)
} }
pathParts := strings.Split(path, ".") pathParts := strings.Split(path, ".")
ext := pathParts[len(pathParts) - 1] ext := pathParts[len(pathParts)-1]
mimeType = mime.TypeByExtension("." + ext) mimeType = mime.TypeByExtension("." + ext)
now := time.Now() now := time.Now()

17
repo.go
View File

@ -11,23 +11,22 @@ import (
) )
var ( var (
pathCache = cache.New(1 * time.Hour, 1 * time.Hour) pathCache = cache.New(1*time.Hour, 1*time.Hour)
) )
type PageCacheEntry struct { type PageCacheEntry struct {
Repository *gitea.Repository Repository *gitea.Repository
Path string Path string
} }
func makePageCacheKey(domain, path string) string { func makePageCacheKey(domain, path string) string {
return domain + "/" + path return domain + "/" + path
} }
/// Try to find the repository with name @reponame of the user @username. If @cname // / Try to find the repository with name @reponame of the user @username. If @cname
/// is not "", then it also verifies that the repository contains a "CNAME" with // / is not "", then it also verifies that the repository contains a "CNAME" with
/// the value of @cname as its content. @host, @domain, and @path are passed for // / the value of @cname as its content. @host, @domain, and @path are passed for
/// caching on success. // / caching on success.
func lookupRepositoryAndCache(username, reponame, host, domain, path, cname string, giteaClient *gitea.Client) (*gitea.Repository, error) { func lookupRepositoryAndCache(username, reponame, host, domain, path, cname string, giteaClient *gitea.Client) (*gitea.Repository, error) {
log.Debugf("Looking up repository %s/%s", username, reponame) log.Debugf("Looking up repository %s/%s", username, reponame)
repo, _, err := giteaClient.GetRepo(username, reponame) repo, _, err := giteaClient.GetRepo(username, reponame)
@ -113,9 +112,9 @@ func RepoFromPath(username, host, cname, path string, giteaClient *gitea.Client)
repoLookup, repoLookup,
) )
reponame = repoLookup reponame = repoLookup
} else if (cname != "") { } else if cname != "" {
// Allow naming the repository "example.org" (But give the TXT record preference) // Allow naming the repository "example.org" (But give the TXT record preference)
reponame = cname; reponame = cname
} }
log.Debugf("Trying repository %s/%s", username, reponame) log.Debugf("Trying repository %s/%s", username, reponame)