env var working, got some validation done before running the cli
This commit is contained in:
parent
92ad51d001
commit
3a65254902
15
Dockerfile
15
Dockerfile
@ -4,25 +4,20 @@ WORKDIR /app
|
||||
COPY . .
|
||||
|
||||
RUN go mod download
|
||||
RUN go build -o /app/local-ip
|
||||
RUN go build
|
||||
|
||||
FROM gcr.io/distroless/base-debian12:latest
|
||||
|
||||
WORKDIR /local-ip
|
||||
|
||||
COPY --from=build /app/local-ip /local-ip/local-ip
|
||||
COPY --from=build /app/local-ip.sh /local-ip/local-ip.sh
|
||||
COPY --from=build /app/http/static /local-ip/http/static
|
||||
|
||||
VOLUME /local-ip/.lego
|
||||
|
||||
# DNS
|
||||
EXPOSE 53/udp
|
||||
# HTTP
|
||||
EXPOSE 80/tcp
|
||||
# HTTPS
|
||||
EXPOSE 443/tcp
|
||||
# DNS HTTP HTTPS
|
||||
EXPOSE 53/udp 80/tcp 443/tcp
|
||||
|
||||
USER root
|
||||
|
||||
# TODO: make these configurable too
|
||||
CMD ["/local-ip/local-ip", "--domain", "local-ip.sh", "--email", "admin@local-ip.sh", "--nameservers", "ns1.local-ip.sh.,ns2.local-ip.sh."]
|
||||
CMD ["/local-ip/local-ip.sh"]
|
||||
|
73
cmd/root.go
73
cmd/root.go
@ -2,29 +2,48 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"local-ip.sh/certs"
|
||||
"local-ip.sh/http"
|
||||
"local-ip.sh/utils"
|
||||
"local-ip.sh/xip"
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: "local-ip.sh",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
nameservers, err := cmd.Flags().GetString("nameservers")
|
||||
if err != nil {
|
||||
utils.Logger.Fatal().Err(err).Msg("Unexpected error")
|
||||
}
|
||||
viper.Set("NameServers", strings.Split(nameservers, ","))
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||
viper.SetEnvPrefix("XIP")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
staging, err := cmd.Flags().GetBool("staging")
|
||||
email := viper.GetString("Email")
|
||||
_, err := mail.ParseAddress(email)
|
||||
if err != nil {
|
||||
utils.Logger.Fatal().Err(err).Msg("Unexpected error")
|
||||
utils.Logger.Fatal().Err(err).Msg("Invalid email address")
|
||||
}
|
||||
|
||||
domain := viper.GetString("Domain")
|
||||
if !govalidator.IsDNSName(domain) {
|
||||
utils.Logger.Fatal().Err(err).Msg("Invalid domain")
|
||||
}
|
||||
|
||||
nameservers := strings.Split(viper.GetString("nameservers"), ",")
|
||||
for _, ns := range nameservers {
|
||||
if !govalidator.IsIPv4(ns) {
|
||||
utils.Logger.Fatal().Err(err).Str("ns", ns).Msg("Invalid name server")
|
||||
}
|
||||
}
|
||||
viper.Set("NameServers", nameservers)
|
||||
|
||||
staging := viper.GetBool("staging")
|
||||
var caDir string
|
||||
if staging {
|
||||
caDir = lego.LEDirectoryStaging
|
||||
@ -35,36 +54,32 @@ var command = &cobra.Command{
|
||||
|
||||
parsedCaDirUrl, _ := url.Parse(caDir)
|
||||
caDirHostname := parsedCaDirUrl.Hostname()
|
||||
email := viper.GetString("Email")
|
||||
|
||||
viper.Set("AccountFilePath", fmt.Sprintf("./.lego/accounts/%s/%s/account.json", caDirHostname, email))
|
||||
viper.Set("KeyFilePath", fmt.Sprintf("./.lego/accounts/%s/%s/keys/%s.key", caDirHostname, email, email))
|
||||
|
||||
utils.InitConfig()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
c := utils.GetConfig()
|
||||
fmt.Printf("%#v\n", c)
|
||||
fmt.Println(c)
|
||||
// n := xip.NewXip()
|
||||
n := xip.NewXip()
|
||||
|
||||
// go func() {
|
||||
// account := certs.LoadAccount()
|
||||
// certsClient := certs.NewCertsClient(n, account)
|
||||
go func() {
|
||||
// try to obtain certificates once the DNS server is accepting requests
|
||||
account := certs.LoadAccount()
|
||||
certsClient := certs.NewCertsClient(n, account)
|
||||
|
||||
// time.Sleep(5 * time.Second)
|
||||
// certsClient.RequestCertificates()
|
||||
time.Sleep(5 * time.Second)
|
||||
certsClient.RequestCertificates()
|
||||
|
||||
// for {
|
||||
// // try to renew certificate every day
|
||||
// time.Sleep(24 * time.Hour)
|
||||
// certsClient.RequestCertificates()
|
||||
// }
|
||||
// }()
|
||||
for {
|
||||
// afterwards, try to renew certificates once a day
|
||||
time.Sleep(24 * time.Hour)
|
||||
certsClient.RequestCertificates()
|
||||
}
|
||||
}()
|
||||
|
||||
// go http.ServeHttp()
|
||||
go http.ServeHttp()
|
||||
|
||||
// n.StartServer()
|
||||
n.StartServer()
|
||||
},
|
||||
}
|
||||
|
||||
@ -82,15 +97,13 @@ func Execute() {
|
||||
viper.BindPFlag("staging", command.Flags().Lookup("staging"))
|
||||
|
||||
command.Flags().String("domain", "", "Root domain (required)")
|
||||
command.MarkFlagRequired("domain")
|
||||
viper.BindPFlag("domain", command.Flags().Lookup("domain"))
|
||||
|
||||
command.Flags().String("email", "", "ACME account email address (required)")
|
||||
command.MarkFlagRequired("email")
|
||||
viper.BindPFlag("email", command.Flags().Lookup("email"))
|
||||
|
||||
command.Flags().String("nameservers", "", "List of nameservers separated by commas")
|
||||
command.MarkFlagRequired("nameservers")
|
||||
command.Flags().String("nameservers", "", "List of nameservers separated by commas (required)")
|
||||
viper.BindPFlag("nameservers", command.Flags().Lookup("nameservers"))
|
||||
|
||||
if err := command.Execute(); err != nil {
|
||||
utils.Logger.Fatal().Err(err).Msg("")
|
||||
|
27
compose.yml
27
compose.yml
@ -1,14 +1,19 @@
|
||||
services:
|
||||
local-ip.sh:
|
||||
image: local-ip.sh
|
||||
build: .
|
||||
volumes:
|
||||
- lego:/local-ip/.lego
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 53:53/udp
|
||||
- 80:80/tcp
|
||||
- 443:443/tcp
|
||||
local-ip.sh:
|
||||
image: local-ip.sh
|
||||
build: .
|
||||
volumes:
|
||||
- lego:/local-ip/.lego
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
XIP_DOMAIN: "local-ip.sh"
|
||||
XIP_EMAIL: "admin@local-ip.sh"
|
||||
XIP_NAMESERVERS: "137.66.40.11,137.66.40.12"
|
||||
# XIP_STAGING: true
|
||||
ports:
|
||||
- 53:53/udp
|
||||
- 80:80/tcp
|
||||
- 443:443/tcp
|
||||
|
||||
volumes:
|
||||
lego:
|
||||
lego:
|
||||
|
9
fly.toml
9
fly.toml
@ -1,8 +1,3 @@
|
||||
# fly.toml app configuration file generated for local-ip-ancient-glade-4376 on 2023-11-29T11:43:10+01:00
|
||||
#
|
||||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||
#
|
||||
|
||||
app = "local-ip"
|
||||
primary_region = "ams"
|
||||
kill_signal = "SIGINT"
|
||||
@ -14,7 +9,9 @@ auto_rollback = true
|
||||
[build]
|
||||
|
||||
[env]
|
||||
PORT = "53"
|
||||
XIP_DOMAIN = "local-ip.sh"
|
||||
XIP_EMAIL = "admin@local-ip.sh"
|
||||
XIP_NAMESERVERS = "137.66.40.11,137.66.40.12" # fly.io edge-only ip addresses, see https://community.fly.io/t/custom-domains-certificate-is-stuck-on-awaiting-configuration/8329
|
||||
|
||||
[mounts]
|
||||
source = "lego"
|
||||
|
5
go.mod
5
go.mod
@ -3,9 +3,12 @@ module local-ip.sh
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/go-acme/lego/v4 v4.10.1
|
||||
github.com/miekg/dns v1.1.57
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
golang.org/x/net v0.23.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
@ -26,9 +29,7 @@ require (
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
|
31
go.sum
31
go.sum
@ -1,11 +1,15 @@
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
||||
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-acme/lego/v4 v4.10.1 h1:MiJvoBXNdmAwEK/SImyhwZ8ZL4IR0jtWDD1wST+N138=
|
||||
@ -13,12 +17,17 @@ github.com/go-acme/lego/v4 v4.10.1/go.mod h1:EMbf0Jmqwv94nJ5WL9qWnSXIBZnvsS9gNyp
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
@ -34,9 +43,11 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
@ -65,8 +76,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
@ -78,8 +87,6 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
@ -87,12 +94,10 @@ golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqR
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -101,14 +106,14 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
|
@ -2,8 +2,11 @@ package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -45,8 +48,9 @@ func registerHandlers() {
|
||||
}
|
||||
|
||||
func serveHttp() *http.Server {
|
||||
utils.Logger.Info().Msg("Starting up HTTP server on :80")
|
||||
httpServer := &http.Server{Addr: ":http"}
|
||||
config := utils.GetConfig()
|
||||
httpServer := &http.Server{Addr: fmt.Sprintf(":%d", config.HttpPort)}
|
||||
utils.Logger.Info().Str("http_address", httpServer.Addr).Msg("Starting up HTTP server")
|
||||
go func() {
|
||||
err := httpServer.ListenAndServe()
|
||||
if err != http.ErrServerClosed {
|
||||
@ -84,22 +88,40 @@ func killServer(httpServer *http.Server) {
|
||||
}
|
||||
|
||||
func redirectHttpToHttps() {
|
||||
utils.Logger.Info().Msg("Redirecting HTTP traffic from :80 to HTTPS :443")
|
||||
config := utils.GetConfig()
|
||||
httpServer := &http.Server{
|
||||
Addr: ":http",
|
||||
Addr: fmt.Sprintf(":%d", config.HttpPort),
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.URL
|
||||
host := r.Host
|
||||
|
||||
// Strip the port from the host if present
|
||||
if strings.Contains(host, ":") {
|
||||
hostWithoutPort, _, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
utils.Logger.Error().Err(err).Msg("Failed to split host and port")
|
||||
} else {
|
||||
host = hostWithoutPort
|
||||
}
|
||||
}
|
||||
// Add the HTTPS port only if it's not 443
|
||||
if config.HttpsPort != 443 {
|
||||
host = net.JoinHostPort(host, strconv.FormatUint(uint64(config.HttpsPort), 10))
|
||||
}
|
||||
|
||||
url.Host = r.Host
|
||||
url.Scheme = "https"
|
||||
http.Redirect(w, r, url.String(), http.StatusMovedPermanently)
|
||||
}),
|
||||
}
|
||||
utils.Logger.Info().Str("http_address", httpServer.Addr).Msg("Redirecting HTTP traffic to HTTPS")
|
||||
go httpServer.ListenAndServe()
|
||||
}
|
||||
|
||||
func serveHttps() {
|
||||
utils.Logger.Info().Msg("Starting up HTTPS server on :443")
|
||||
httpsServer := &http.Server{Addr: ":https"}
|
||||
config := utils.GetConfig()
|
||||
httpsServer := &http.Server{Addr: fmt.Sprintf(":%d", config.HttpsPort)}
|
||||
utils.Logger.Info().Str("https_address", httpsServer.Addr).Msg("Starting up HTTPS server")
|
||||
go httpsServer.ListenAndServeTLS("./.lego/certs/root/server.pem", "./.lego/certs/root/server.key")
|
||||
}
|
||||
|
||||
|
105
xip/records.go
105
xip/records.go
@ -1,11 +1,9 @@
|
||||
package xip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"local-ip.sh/utils"
|
||||
)
|
||||
|
||||
type hardcodedRecord struct {
|
||||
@ -17,41 +15,8 @@ type hardcodedRecord struct {
|
||||
SRV *dns.SRV
|
||||
}
|
||||
|
||||
var config = utils.GetConfig()
|
||||
var hardcodedRecords = map[string]hardcodedRecord{
|
||||
// TODO: maybe --nameservers ns1.local-ip.sh.=137.66.40.11,ns2.local-ip.sh.=137.66.40.12
|
||||
fmt.Sprintf("ns.%s.", config.Domain): {
|
||||
// record holding ip addresses of ns1 and ns2
|
||||
A: []net.IP{
|
||||
net.IPv4(137, 66, 40, 11),
|
||||
net.IPv4(137, 66, 40, 12),
|
||||
},
|
||||
},
|
||||
fmt.Sprintf("ns1.%s.", config.Domain): {
|
||||
A: []net.IP{
|
||||
net.IPv4(137, 66, 40, 11), // fly.io edge-only ip address, see https://community.fly.io/t/custom-domains-certificate-is-stuck-on-awaiting-configuration/8329
|
||||
},
|
||||
},
|
||||
fmt.Sprintf("ns2.%s.", config.Domain): {
|
||||
A: []net.IP{
|
||||
net.IPv4(137, 66, 40, 12), // fly.io edge-only ip address #2
|
||||
},
|
||||
},
|
||||
fmt.Sprintf("%s.", config.Domain): {
|
||||
// same as ns.local-ip.sh, it's the same machine :)
|
||||
A: []net.IP{
|
||||
net.IPv4(137, 66, 40, 11),
|
||||
net.IPv4(137, 66, 40, 12),
|
||||
},
|
||||
},
|
||||
fmt.Sprintf("_acme-challenge.%s.", config.Domain): {
|
||||
// will be filled in later when requesting the wildcard certificate
|
||||
TXT: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
// additional records I set up to host emails, feel free to change or remove them for your own needs
|
||||
var extraRecords = map[string]hardcodedRecord{
|
||||
var records = map[string]hardcodedRecord{
|
||||
// additional records I set up to host emails, feel free to change or remove them for your own needs
|
||||
"local-ip.sh.": {
|
||||
TXT: []string{"v=spf1 include:capsulecorp.dev ~all"},
|
||||
MX: []*dns.MX{
|
||||
@ -85,69 +50,3 @@ var extraRecords = map[string]hardcodedRecord{
|
||||
},
|
||||
},
|
||||
}
|
||||
var records = mergeRecords(hardcodedRecords, extraRecords)
|
||||
|
||||
func mergeRecords(a, b map[string]hardcodedRecord) map[string]hardcodedRecord {
|
||||
result := make(map[string]hardcodedRecord)
|
||||
for k, v := range a {
|
||||
result[k] = v
|
||||
}
|
||||
for k, v := range b {
|
||||
if r, ok := result[k]; ok {
|
||||
result[k] = hardcodedRecord{
|
||||
A: uniqueIPs(append(r.A, v.A...)),
|
||||
AAAA: uniqueIPs(append(r.AAAA, v.AAAA...)),
|
||||
TXT: uniqueStrings(append(r.TXT, v.TXT...)),
|
||||
MX: uniqueMX(append(r.MX, v.MX...)),
|
||||
CNAME: uniqueStrings(append(r.CNAME, v.CNAME...)),
|
||||
SRV: firstNonNil(r.SRV, v.SRV),
|
||||
}
|
||||
} else {
|
||||
result[k] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func uniqueIPs(ips []net.IP) []net.IP {
|
||||
seen := make(map[string]bool)
|
||||
result := []net.IP{}
|
||||
for _, ip := range ips {
|
||||
if !seen[ip.String()] {
|
||||
seen[ip.String()] = true
|
||||
result = append(result, ip)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func uniqueStrings(strs []string) []string {
|
||||
seen := make(map[string]bool)
|
||||
result := []string{}
|
||||
for _, str := range strs {
|
||||
if !seen[str] {
|
||||
seen[str] = true
|
||||
result = append(result, str)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func uniqueMX(mxs []*dns.MX) []*dns.MX {
|
||||
seen := make(map[string]uint16)
|
||||
result := []*dns.MX{}
|
||||
for _, mx := range mxs {
|
||||
if pref, exists := seen[mx.Mx]; !exists || pref > mx.Preference {
|
||||
seen[mx.Mx] = mx.Preference
|
||||
result = append(result, mx)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func firstNonNil[T any](a, b *T) *T {
|
||||
if a != nil {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
50
xip/xip.go
50
xip/xip.go
@ -14,7 +14,7 @@ import (
|
||||
|
||||
type Xip struct {
|
||||
server dns.Server
|
||||
nameServers []*dns.NS
|
||||
nameServers []string
|
||||
}
|
||||
|
||||
var (
|
||||
@ -25,6 +25,7 @@ var (
|
||||
|
||||
func (xip *Xip) SetTXTRecord(fqdn string, value string) {
|
||||
utils.Logger.Debug().Str("fqdn", fqdn).Str("value", value).Msg("Trying to set TXT record")
|
||||
config := utils.GetConfig()
|
||||
if fqdn != fmt.Sprintf("_acme-challenge.%s.", config.Domain) {
|
||||
utils.Logger.Debug().Msg("Not allowed, abort")
|
||||
return
|
||||
@ -38,6 +39,7 @@ func (xip *Xip) SetTXTRecord(fqdn string, value string) {
|
||||
|
||||
func (xip *Xip) UnsetTXTRecord(fqdn string) {
|
||||
utils.Logger.Debug().Str("fqdn", fqdn).Msg("Trying to set TXT record")
|
||||
config := utils.GetConfig()
|
||||
if fqdn != fmt.Sprintf("_acme-challenge.%s.", config.Domain) {
|
||||
utils.Logger.Debug().Msg("Not allowed, abort")
|
||||
return
|
||||
@ -145,10 +147,10 @@ func (xip *Xip) handleNS(question dns.Question, message *dns.Msg) {
|
||||
Rrtype: dns.TypeNS,
|
||||
Class: dns.ClassINET,
|
||||
},
|
||||
Ns: ns.Ns,
|
||||
Ns: ns,
|
||||
})
|
||||
|
||||
additionals = append(additionals, xip.fqdnToA(ns.Ns)...)
|
||||
additionals = append(additionals, xip.fqdnToA(ns)...)
|
||||
}
|
||||
|
||||
for _, record := range nameServers {
|
||||
@ -257,7 +259,15 @@ func (xip *Xip) handleSOA(question dns.Question, message *dns.Msg) {
|
||||
message.Answer = append(message.Answer, xip.soaRecord(question))
|
||||
}
|
||||
|
||||
func emailToRname(email string) string {
|
||||
parts := strings.SplitN(email, "@", 2)
|
||||
localPart := strings.ReplaceAll(parts[0], ".", "\\.")
|
||||
domain := parts[1]
|
||||
return localPart + "." + domain + "."
|
||||
}
|
||||
|
||||
func (xip *Xip) soaRecord(question dns.Question) *dns.SOA {
|
||||
config := utils.GetConfig()
|
||||
soa := new(dns.SOA)
|
||||
soa.Hdr = dns.RR_Header{
|
||||
Name: question.Name,
|
||||
@ -266,8 +276,8 @@ func (xip *Xip) soaRecord(question dns.Question) *dns.SOA {
|
||||
Ttl: uint32((time.Minute * 5).Seconds()),
|
||||
Rdlength: 0,
|
||||
}
|
||||
soa.Ns = xip.nameServers[0].Ns
|
||||
soa.Mbox = config.Email
|
||||
soa.Ns = xip.nameServers[0]
|
||||
soa.Mbox = emailToRname(config.Email)
|
||||
soa.Serial = 2024072200
|
||||
soa.Refresh = uint32((time.Minute * 15).Seconds())
|
||||
soa.Retry = uint32((time.Minute * 15).Seconds())
|
||||
@ -321,6 +331,7 @@ func (xip *Xip) handleDnsRequest(response dns.ResponseWriter, request *dns.Msg)
|
||||
|
||||
error := response.WriteMsg(message)
|
||||
if error != nil {
|
||||
utils.Logger.Debug().Msg(message.String())
|
||||
utils.Logger.Error().Err(error).Str("message", message.String()).Msg("Error responding to query")
|
||||
}
|
||||
}()
|
||||
@ -335,6 +346,7 @@ func (xip *Xip) StartServer() {
|
||||
err := xip.server.ListenAndServe()
|
||||
defer xip.server.Shutdown()
|
||||
if err != nil {
|
||||
utils.Logger.Fatal().Err(err).Msg("Failed to start DNS server")
|
||||
if strings.Contains(err.Error(), "fly-global-services: no such host") {
|
||||
// we're not running on fly, bind to 0.0.0.0 instead
|
||||
port := strings.Split(xip.server.Addr, ":")[1]
|
||||
@ -349,16 +361,36 @@ func (xip *Xip) StartServer() {
|
||||
|
||||
utils.Logger.Fatal().Err(err).Msg("Failed to start DNS server")
|
||||
}
|
||||
utils.Logger.Info().Str("dns_address", xip.server.Addr).Msg("DNS server listening")
|
||||
utils.Logger.Info().Str("dns_address", xip.server.Addr).Msg("Starting up DNS server")
|
||||
}
|
||||
|
||||
func (xip *Xip) initHardcodedRecords() {
|
||||
config := utils.GetConfig()
|
||||
rootDomainARecords := []net.IP{}
|
||||
|
||||
for i, ns := range config.NameServers {
|
||||
name := fmt.Sprintf("ns%d.%s.", i+1, config.Domain)
|
||||
ip := net.ParseIP(ns)
|
||||
|
||||
rootDomainARecords = append(rootDomainARecords, ip)
|
||||
entry := records[name]
|
||||
entry.A = append(records[name].A, ip)
|
||||
records[name] = entry
|
||||
|
||||
xip.nameServers = append(xip.nameServers, name)
|
||||
}
|
||||
|
||||
records[fmt.Sprintf("%s.", config.Domain)] = hardcodedRecord{A: rootDomainARecords}
|
||||
|
||||
// will be filled in later when requesting the wildcard certificate
|
||||
records[fmt.Sprintf("_acme-challenge.%s.", config.Domain)] = hardcodedRecord{TXT: []string{}}
|
||||
}
|
||||
|
||||
func NewXip() (xip *Xip) {
|
||||
config := utils.GetConfig()
|
||||
xip = &Xip{}
|
||||
|
||||
for _, ns := range config.NameServers {
|
||||
xip.nameServers = append(xip.nameServers, &dns.NS{Ns: ns})
|
||||
}
|
||||
xip.initHardcodedRecords()
|
||||
|
||||
xip.server = dns.Server{
|
||||
Addr: fmt.Sprintf(":%d", config.DnsPort),
|
||||
|
Loading…
Reference in New Issue
Block a user