diff --git a/.dockerignore b/.dockerignore index 2eea525..cdf0366 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +certs \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2eea525..cdf0366 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +certs \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 21c2618..8451363 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,16 @@ -FROM golang:1.22-alpine as build +FROM docker.io/golang:1.22-alpine as build WORKDIR /app COPY . . ENV GOCACHE=/root/.cache/go-build -RUN --mount=type=cache,target="/root/.cache/go-build" go build . -RUN --mount=type=cache,target="/root/.cache/go-build" go build -C cert . +RUN --mount=type=cache,target="/root/.cache/go-build" go build -ldflags="-w -s" -gcflags=all=-l . -FROM nginx:alpine-slim +FROM scratch COPY --from=build app app +# copy ssl for work HTTPS requests to gitea instance +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ WORKDIR /app -COPY nginx.conf /etc/nginx/nginx.conf - -ENTRYPOINT [ "sh", "entrypoint.sh" ] \ No newline at end of file +ENV GIN_MODE=release +ENTRYPOINT [ "./giteapages" ] \ No newline at end of file diff --git a/README.md b/README.md index fe4be18..d486023 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,38 @@ +## currently hosted by me on +https://pages.mi6e4ka.dev + # how to run -`docker run -p 443:443 -p 80:80 --name gitea-pages -v gitea-pages-ssl:/etc/ssl -d gitea-pages` -## gen ssl (first run) -`docker exec -it gp /app/cert/cert root.domain` -and restart docker container! \ No newline at end of file + +`docker run -p 8080:8080 --name gitea-pages -d git.mi6e4ka.dev/mi6e4ka/gitea-pages` +``` +issue wildcard ssl for domains *.rootdomain and rootdomain should be your reverse proxy +``` +or by docker-compose +``` +services: + gitea-pages: + env_file: ".env" + image: git.mi6e4ka.dev/mi6e4ka/gitea-pages + ports: + - 8080:8080 +``` + +## ENV SETTINGS + +also by create .env file + + +param | default | eg | description +--- | --- | ---| --- +ROOT_DOMAIN | localhost | my.pages | root domain to serve sites +DEFAULT_USER | | mi6e4ka | Gitea user if not specified user subdomain +GITEA_URL | https://codeberg.org | | Gitea instance with api +INDEX_FILE | index.html | | File serving by default +DIRECTORY_INDEX | false | true | Show list files if not index.html in directory +SERVE_PORT | 8080 | | Port to listen +GITEA_TOKEN | | 5yE5j5r1qHZI | For access to private repos \ No newline at end of file diff --git a/cert/cert.go b/cert.go similarity index 50% rename from cert/cert.go rename to cert.go index e19d71c..28f003e 100644 --- a/cert/cert.go +++ b/cert.go @@ -7,12 +7,14 @@ import ( "crypto/rand" "fmt" "log" + "net/url" "os" + "time" "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certificate" - "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/providers/dns/pdns" "github.com/go-acme/lego/v4/registration" ) @@ -33,60 +35,62 @@ func (u *MyUser) GetPrivateKey() crypto.PrivateKey { return u.key } -type DNSProviderBestDNS struct { - apiAuthToken string -} - -func NewDNSProviderBestDNS(apiAuthToken string) (*DNSProviderBestDNS, error) { - return &DNSProviderBestDNS{apiAuthToken: apiAuthToken}, nil -} -func (d *DNSProviderBestDNS) Present(domain, token, keyAuth string) error { - info := dns01.GetChallengeInfo(domain, keyAuth) - fmt.Println() - fmt.Println("------") - fmt.Println() - fmt.Println("Please create DNS TXT record, for domain", info.FQDN, "with these content:") - fmt.Println() - fmt.Println(info.Value) - fmt.Println() - fmt.Println("------") - fmt.Scanln() - // make API request to set a TXT record on fqdn with value and TTL - return nil -} -func (d *DNSProviderBestDNS) CleanUp(domain, token, keyAuth string) error { - // clean up any state you created in Present, like removing the TXT record - fmt.Println("------") - fmt.Println() - fmt.Println("you can delete the TXT record, and press enter to continue") - fmt.Println() - fmt.Println("------") - fmt.Scanln() - return nil -} - -func main() { - if len(os.Args) != 2 { - fmt.Println("usage: ./cert root.domain") +func GenCerts() { + testMode := false + testModeStr, exists := os.LookupEnv("TEST_MODE") + if !exists { + if testModeStr == "true" { + testMode = true + } + } + rootDomain, exists := os.LookupEnv("ROOT_DOMAIN") + if !exists { + rootDomain = "localhost" + } + pdnsKey, has := os.LookupEnv("PDNS_KEY") + if !has { + fmt.Println("must have PDNS_KEY") os.Exit(1) } - fmt.Println("root domain is", os.Args[1]) + pdnsHost, has := os.LookupEnv("PDNS_HOST") + if !has { + fmt.Println("must have PDNS_HOST ENV") + os.Exit(1) + } + acmeEmail, has := os.LookupEnv("ACME_EMAIL") + if !has { + fmt.Println("must have ACME_EMAIL ENV") + os.Exit(1) + } + fmt.Println("root domain is", rootDomain) // Create a user. New accounts need an email and private key to start. privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { log.Fatal(err) } - var email string - fmt.Print("enter you email > ") - fmt.Scanln(&email) myUser := MyUser{ - Email: email, + Email: acmeEmail, key: privateKey, } + pdnsUrl, _ := url.Parse(pdnsHost) + provider, err := pdns.NewDNSProviderConfig(&pdns.Config{ + Host: pdnsUrl, + APIKey: pdnsKey, + ServerName: "localhost", + TTL: 5, + PropagationTimeout: time.Duration(time.Second * 30), + PollingInterval: time.Duration(time.Millisecond * 500), + }) + if err != nil { + log.Fatal(err) + } config := lego.NewConfig(&myUser) + // uncoment for tesing ->> + if testMode { + config.CADirURL = lego.LEDirectoryStaging + } - //config.CADirURL = lego.LEDirectoryStaging config.Certificate.KeyType = certcrypto.RSA2048 // A client facilitates communication with the CA server. @@ -94,15 +98,7 @@ func main() { if err != nil { log.Fatal(err) } - - bestDNS, err := NewDNSProviderBestDNS("my-auth-token") - if err != nil { - log.Fatal(err) - } - err = client.Challenge.SetDNS01Provider(bestDNS) - if err != nil { - log.Fatal(err) - } + client.Challenge.SetDNS01Provider(provider) // New users will need to register reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) @@ -112,7 +108,7 @@ func main() { myUser.Registration = reg request := certificate.ObtainRequest{ - Domains: []string{os.Args[1], "*." + os.Args[1]}, + Domains: []string{rootDomain, "*." + rootDomain}, Bundle: true, } certificates, err := client.Certificate.Obtain(request) @@ -122,9 +118,9 @@ func main() { // Each certificate comes back with the cert bytes, the bytes of the client's // private key, and a certificate URL. SAVE THESE TO DISK. - os.Mkdir("/etc/ssl", 0777) - os.WriteFile("/etc/ssl/cert.crt", certificates.Certificate, 0777) - os.WriteFile("/etc/ssl/priv.key", certificates.PrivateKey, 0777) + os.Mkdir("./certs", 0777) + os.WriteFile("./certs/cert.crt", certificates.Certificate, 0777) + os.WriteFile("./certs/priv.key", certificates.PrivateKey, 0777) // ... all done. } diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 438a9d3..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -if [ -e /etc/ssl/cert.crt ] && [ -e /etc/ssl/priv.key ] -then - nginx & GIN_MODE=release ./giteapages -else - echo "create certs first" - tail -f /dev/null -fi diff --git a/main.go b/main.go index 9a81b49..1a84af6 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "embed" "encoding/json" + "fmt" "html/template" "io" "mime" @@ -32,6 +33,29 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { func main() { godotenv.Load() + // if os.Args[len(os.Args)-1] == "gencrt" { + // GenCerts() + // os.Exit(0) + // } + // if _, err := os.Stat("./certs/cert.crt"); err != nil { + // fmt.Println("Not found cert file") + // GenCerts() + // } + // if _, err := os.Stat("./certs/priv.key"); err != nil { + // fmt.Println("Not fount cert key file") + // GenCerts() + // } + //certFile, _ := os.ReadFile("./certs/cert.crt") + //block, _ := pem.Decode(certFile) + //if block == nil { + // fmt.Println("invalid certificate") + // GenCerts() + // } + // cert, _ := x509.ParseCertificate(block.Bytes) + // certExpired := time.Now().After(cert.NotAfter) + // if certExpired { + // GenCerts() + // } rootDomain, exists := os.LookupEnv("ROOT_DOMAIN") if !exists { rootDomain = "localhost" @@ -49,9 +73,10 @@ func main() { indexFile = "index.html" } directoryIndexStr := os.Getenv("DIRECTORY_INDEX") - directoryIndex := true - if directoryIndexStr == "false" { - directoryIndex = false + fmt.Println(rootDomain, giteaUrl) + directoryIndex := false + if directoryIndexStr == "true" { + directoryIndex = true } defaultUser := os.Getenv("DEFAULT_USER") servePort, exists := os.LookupEnv("SERVE_PORT") diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index 0a911d9..0000000 --- a/nginx.conf +++ /dev/null @@ -1,47 +0,0 @@ -worker_processes 1; - -events { worker_connections 1024; } - -http { - - sendfile on; - large_client_header_buffers 4 32k; - - upstream web-api { - server localhost:8080; - } - - server { - listen 80 default_server; - server_name _; - - location / { - return 301 https://$host$request_uri; - } - } - - server { - listen 443 ssl; - server_name _; - - ssl_certificate /etc/ssl/cert.crt; - ssl_certificate_key /etc/ssl/priv.key; - - location / { - proxy_pass http://web-api; - proxy_redirect off; - proxy_http_version 1.1; - proxy_cache_bypass $http_upgrade; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection keep-alive; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $server_name; - proxy_buffer_size 128k; - proxy_buffers 4 256k; - proxy_busy_buffers_size 256k; - } - } -} \ No newline at end of file