-
Notifications
You must be signed in to change notification settings - Fork 22
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
1 parent
b32ef23
commit c8e2a5c
Showing
7 changed files
with
283 additions
and
58 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 |
---|---|---|
|
@@ -22,6 +22,7 @@ goshs is a replacement for Python's `SimpleHTTPServer`. It allows uploading and | |
* Basic Authentication | ||
* Transport Layer Security (HTTPS) | ||
* self-signed | ||
* let's encrypt | ||
* provide own certificate | ||
* Non persistent clipboard | ||
* Download clipboard entries as .json file | ||
|
@@ -83,10 +84,16 @@ Web server options: | |
-c, --cli Enable cli (only with auth and tls) (default: false) | ||
|
||
TLS options: | ||
-s, --ssl Use TLS | ||
-ss, --self-signed Use a self-signed certificate | ||
-sk, --server-key Path to server key | ||
-sc, --server-cert Path to server certificate | ||
-s, --ssl Use TLS | ||
-ss, --self-signed Use a self-signed certificate | ||
-sk, --server-key Path to server key | ||
-sc, --server-cert Path to server certificate | ||
-sl, --lets-encrypt Use Let's Encrypt as certification service | ||
-sld, --le-domains Domain(s) to request from Let's Encrypt (comma separated list) | ||
-sle, --le-email Email to use with Let's Encrypt | ||
-slh, --le-http Port to use for Let's Encrypt HTTP Challenge (default: 80) | ||
-slt, --le-tls Port to use for Let's Encrypt TLS ALPN Challenge (default: 443) | ||
Authentication options: | ||
-b, --basic-auth Use basic authentication (user:pass - user can be empty) | ||
|
@@ -102,10 +109,11 @@ Usage examples: | |
Start with wevdav support: ./goshs -w | ||
Start with different port: ./goshs -p 8080 | ||
Start with self-signed cert: ./goshs -s -ss | ||
Start with let's encrypt: ./goshs -s -sl -sle [email protected] -sld your.domain.com,your.seconddomain.com | ||
Start with custom cert: ./goshs -s -sk <path to key> -sc <path to cert> | ||
Start with basic auth: ./goshs -b secret-user:$up3r$3cur3 | ||
Start with basic auth empty user: ./goshs -b :$up3r$3cur3 | ||
Start with cli enabled: ./goshs -b secret-user:$up3r$3cur3 -s -ss -c | ||
Start with cli enabled: ./goshs -b secret-user:$up3r$3cur3 -s -ss -c | ||
``` | ||
|
||
# Examples | ||
|
@@ -138,6 +146,20 @@ Usage examples: | |
|
||
`goshs -s -ss` | ||
|
||
*Let's encrypt* | ||
|
||
`./goshs -s -sl -sle [email protected] -sld your.domain.com,your.seconddomain.com` | ||
|
||
You will have to make sure that your IP is reachable via the domain name by creating an A entry with you DNS service provider first. | ||
|
||
Then the example command will create two files called `key` and `cert` if the request for a certificate is successful. *Please note:* for this to work let's encrypt needs to reach goshs at port 80 and 443. So you will need to start it as root. There are several options you can choose from to circumvent running goshs as root after obtaining a valid certificate: | ||
|
||
- Drop user privileges using `-u` (preferred) | ||
- Run it once as root until you obtain the certificate. Then stop it and rerun it using `key` and `cert` like: `./goshs -s -sk key -sc cert` as non-root user | ||
- Use `-slh` and `-slt` to choose different challenge ports and proxy port 80 and 443 to them | ||
|
||
After stopping goshs you can reuse the files `key` and `cert` to restart the server with a valid certificate like: `./goshs -s -sk key -sc -cert` until they are invalidated due to certificate lifetime (90 days). | ||
|
||
*Provide own certificate* | ||
|
||
`goshs -s -sk server.key -sc server.crt` | ||
|
@@ -150,6 +172,8 @@ This mode will omit the dir listing on the web interface. Also you will not have | |
**Retrieve the directory listing in json format** | ||
You can now retrieve the directory listing in *json* format. This is meant to be used with curl for example in environments where you do not have a browser on hand. | ||
|
||
|
||
|
||
```bash | ||
curl -s localhost:8000/?json | jq | ||
[ | ||
|
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,142 @@ | ||
package ca | ||
|
||
import ( | ||
"bufio" | ||
"crypto" | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/go-acme/lego/v4/certcrypto" | ||
"github.com/go-acme/lego/v4/certificate" | ||
"github.com/go-acme/lego/v4/challenge/http01" | ||
"github.com/go-acme/lego/v4/challenge/tlsalpn01" | ||
"github.com/go-acme/lego/v4/lego" | ||
"github.com/go-acme/lego/v4/registration" | ||
"github.com/patrickhener/goshs/logger" | ||
) | ||
|
||
// LetsEncryptUser is the struct of all information needed to rollout a lets encrypt certificate | ||
type LetsEncryptUser struct { | ||
Email string | ||
Registration *registration.Resource | ||
Key crypto.PrivateKey | ||
HTTPPort string | ||
TLSPort string | ||
Domains []string | ||
Config *lego.Config | ||
Client *lego.Client | ||
} | ||
|
||
// GetEmail will return the Users Email | ||
func (u *LetsEncryptUser) GetEmail() string { | ||
return u.Email | ||
} | ||
|
||
// Get Registration will return the Registration | ||
func (u *LetsEncryptUser) GetRegistration() *registration.Resource { | ||
return u.Registration | ||
} | ||
|
||
// GetPrivateKey will return the Private Key | ||
func (u *LetsEncryptUser) GetPrivateKey() crypto.PrivateKey { | ||
return u.Key | ||
} | ||
|
||
func (u *LetsEncryptUser) RequestCertificate() ([]byte, []byte) { | ||
// Create a user. New accounts need an email and private key to start. | ||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
if err != nil { | ||
logger.Fatalf("error generating private key for lets encrypt: %+v", err) | ||
} | ||
|
||
u.Key = privateKey | ||
|
||
u.Config = lego.NewConfig(u) | ||
|
||
// This CA URL is configured for a local dev instance of Boulder running in Docker in a VM. | ||
u.Config.Certificate.KeyType = certcrypto.RSA2048 | ||
|
||
// A client facilitates communication with the CA server. | ||
u.Client, err = lego.NewClient(u.Config) | ||
if err != nil { | ||
logger.Fatalf("error retrieving client for communication with lets encrypt acme server: %+v", err) | ||
} | ||
|
||
err = u.Client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", u.TLSPort)) | ||
if err != nil { | ||
logger.Fatalf("error setting tls alpn provider for lets encrypt: %+v", err) | ||
} | ||
|
||
err = u.Client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", u.HTTPPort)) | ||
if err != nil { | ||
logger.Fatalf("error setting http provider for lets encrypt: %+v", err) | ||
} | ||
|
||
// New users will need to register | ||
reg, err := u.Client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true}) | ||
if err != nil { | ||
logger.Fatalf("error registering new user with lets encrypt: %+v", err) | ||
} | ||
u.Registration = reg | ||
|
||
request := certificate.ObtainRequest{ | ||
Domains: u.Domains, | ||
Bundle: true, | ||
} | ||
|
||
certificates, err := u.Client.Certificate.Obtain(request) | ||
if err != nil { | ||
logger.Fatalf("error registering domain(s) with lets encrypt: %+v", err) | ||
} | ||
|
||
return certificates.PrivateKey, certificates.Certificate | ||
} | ||
|
||
func GetLECertificateAndKey(email string, domains []string, httpPort string, tlsPort string) { | ||
// Get email if none provided | ||
if email == "" { | ||
reader := bufio.NewReader(os.Stdin) | ||
fmt.Print("Provide E-Mail to use with Let's Encrypt: ") | ||
resultEmail, err := reader.ReadString('\n') | ||
if err != nil { | ||
logger.Fatalf("error reading email from stdin: %+v", err) | ||
} | ||
email = strings.Trim(resultEmail, "\n") | ||
} | ||
|
||
// Get domains if none are provided | ||
if domains[0] == "" { | ||
reader := bufio.NewReader(os.Stdin) | ||
fmt.Print("Provide domain(s) to request with Let's Encrypt: ") | ||
resultDomains, err := reader.ReadString('\n') | ||
if err != nil { | ||
logger.Fatalf("error reading domains from stdin: %+v", err) | ||
} | ||
resultDomains = strings.Trim(resultDomains, "\n") | ||
domains = strings.Split(resultDomains, ",") | ||
} | ||
|
||
// construct letsencrypt user object | ||
letsencryptUser := LetsEncryptUser{ | ||
Email: email, | ||
HTTPPort: httpPort, | ||
TLSPort: tlsPort, | ||
Domains: domains, | ||
} | ||
|
||
key, cert := letsencryptUser.RequestCertificate() | ||
|
||
err := os.WriteFile("key", key, 0644) | ||
if err != nil { | ||
logger.Fatalf("error writing file 'key': %+v", err) | ||
} | ||
|
||
err = os.WriteFile("cert", cert, 0644) | ||
if err != nil { | ||
logger.Fatalf("error writing file 'cert': %+v", err) | ||
} | ||
} |
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 |
---|---|---|
@@ -1,17 +1,26 @@ | ||
module github.com/patrickhener/goshs | ||
|
||
go 1.19 | ||
go 1.21 | ||
|
||
toolchain go1.21.8 | ||
|
||
require ( | ||
github.com/go-acme/lego/v4 v4.16.1 | ||
github.com/gorilla/mux v1.8.0 | ||
github.com/gorilla/websocket v1.4.2 | ||
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef | ||
github.com/sirupsen/logrus v1.8.1 | ||
github.com/sirupsen/logrus v1.9.3 | ||
golang.org/x/crypto v0.21.0 | ||
golang.org/x/net v0.23.0 | ||
) | ||
|
||
require ( | ||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect | ||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect | ||
github.com/miekg/dns v1.1.58 // indirect | ||
golang.org/x/mod v0.14.0 // indirect | ||
golang.org/x/sys v0.18.0 // indirect | ||
golang.org/x/term v0.18.0 // indirect | ||
golang.org/x/text v0.14.0 // indirect | ||
golang.org/x/tools v0.17.0 // indirect | ||
) |
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 |
---|---|---|
@@ -1,23 +1,48 @@ | ||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= | ||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= | ||
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/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ= | ||
github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE= | ||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= | ||
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= | ||
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/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= | ||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | ||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= | ||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= | ||
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= | ||
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= | ||
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= | ||
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/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= | ||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= | ||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= | ||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= | ||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
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/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= | ||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= | ||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= | ||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= | ||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= | ||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= | ||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= | ||
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.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= | ||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
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
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
Oops, something went wrong.