Parse configuration file

This commit is contained in:
Trevor Slocum 2020-11-24 09:29:57 -08:00
parent 9cf58a9982
commit d17e5ab9cf
7 changed files with 140 additions and 9 deletions

33
CONFIGURATION.md Normal file
View file

@ -0,0 +1,33 @@
`gmitohtml` loads its configuration from `~/.config/gmitohtml/config.yaml` by
default. You may specify a different location via the `--config` argument.
# Configuration options
## Client certificates
Client certificates may be specified via the `Certs` option.
To generate a client certificate, run the following:
```bash
openssl req -x509 -out localhost.crt -keyout localhost.key \
-newkey rsa:2048 -nodes -sha256 \
-subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
```
Files `localhost.crt` and `localhost.key` are generated. Rename these files to
match the domain where the certificate will be used.
# Example config.yaml
```yaml
certs:
astrobotany.mozz.us:
cert: /home/dioscuri/.config/gmitohtml/astrobotany.mozz.us.crt
key: /home/dioscuri/.config/gmitohtml/astrobotany.mozz.us.crt
gemini.rocks:
cert: /home/dioscuri/.config/gmitohtml/gemini.rocks.crt
key: /home/dioscuri/.config/gmitohtml/gemini.rocks.key
```

View file

@ -17,6 +17,10 @@ go get gitlab.com/tslocum/gmitohtml
The resulting binary is available as `~/go/bin/gmitohtml`.
## Configure
See [CONFIGURATION.md](https://gitlab.com/tslocum/gmitohtml/blob/master/CONFIGURATION.md)
## Usage
Run daemon at [http://localhost:1967](http://localhost:1967):

44
config.go Normal file
View file

@ -0,0 +1,44 @@
package main
import (
"crypto/tls"
"errors"
"io/ioutil"
"gopkg.in/yaml.v3"
)
type certConfig struct {
Cert string
Key string
cert tls.Certificate
}
type appConfig struct {
Certs map[string]*certConfig
}
var config = &appConfig{
Certs: make(map[string]*certConfig),
}
func readconfig(configPath string) error {
if configPath == "" {
return errors.New("file unspecified")
}
configData, err := ioutil.ReadFile(configPath)
if err != nil {
return err
}
var newConfig *appConfig
err = yaml.Unmarshal(configData, &newConfig)
if err != nil {
return err
}
config = newConfig
return nil
}

2
go.mod
View file

@ -1,3 +1,5 @@
module gitlab.com/tslocum/gmitohtml
go 1.15
require gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776

4
go.sum
View file

@ -0,0 +1,4 @@
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

40
main.go
View file

@ -1,6 +1,8 @@
package main
import (
"path"
"gitlab.com/tslocum/gmitohtml/pkg/gmitohtml"
"flag"
@ -33,11 +35,47 @@ func openBrowser(url string) {
func main() {
var view bool
var daemon string
var configFile string
flag.BoolVar(&view, "view", false, "open web browser")
flag.StringVar(&daemon, "daemon", "", "start daemon on specified address")
flag.StringVar(&configFile, "config", "", "path to configuration file")
// TODO option to include response header in page
flag.Parse()
if configFile == "" {
homedir, err := os.UserHomeDir()
if err == nil && homedir != "" {
defaultConfig := path.Join(homedir, ".config", "gmitohtml", "config.yaml")
if _, err := os.Stat(defaultConfig); !os.IsNotExist(err) {
configFile = defaultConfig
}
}
}
if configFile != "" {
err := readconfig(configFile)
if err != nil {
log.Fatalf("failed to read configuration file at %s: %v\nSee CONFIGURATION.md for information on configuring gmitohtml", configFile, err)
}
}
for domain, cc := range config.Certs {
certData, err := ioutil.ReadFile(cc.Cert)
if err != nil {
log.Fatalf("failed to load client certificate for domain %s: %s", domain, err)
}
keyData, err := ioutil.ReadFile(cc.Key)
if err != nil {
log.Fatalf("failed to load client certificate for domain %s: %s", domain, err)
}
err = gmitohtml.SetClientCertificate(domain, certData, keyData)
if err != nil {
log.Fatalf("failed to load client certificate for domain %s", domain)
}
}
if daemon != "" {
err := gmitohtml.StartDaemon(daemon)
if err != nil {
@ -48,7 +86,7 @@ func main() {
openBrowser("http://" + daemon)
}
select {} //TODO
select {}
}
data, err := ioutil.ReadAll(os.Stdin)

View file

@ -3,6 +3,7 @@ package gmitohtml
import (
"bytes"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
@ -147,13 +148,6 @@ func handleRequest(writer http.ResponseWriter, request *http.Request) {
return
}
inputText := request.PostFormValue("input")
if inputText != "" {
request.URL.RawQuery = inputText
http.Redirect(writer, request, request.URL.String(), http.StatusSeeOther)
return
}
pathSplit := strings.Split(request.URL.Path, "/")
if len(pathSplit) < 2 || pathSplit[1] != "gemini" {
writer.Write([]byte("Error: invalid protocol, only Gemini is supported"))
@ -169,6 +163,13 @@ func handleRequest(writer http.ResponseWriter, request *http.Request) {
u.RawQuery = request.URL.RawQuery
}
inputText := request.PostFormValue("input")
if inputText != "" {
u.RawQuery = inputText
http.Redirect(writer, request, rewriteURL(u.String(), u), http.StatusSeeOther)
return
}
header, data, err := fetch(u.String())
if err != nil {
fmt.Fprintf(writer, "Error: failed to fetch %s: %s", u, err)
@ -178,7 +179,7 @@ func handleRequest(writer http.ResponseWriter, request *http.Request) {
if len(header) > 0 && header[0] == '3' {
split := bytes.SplitN(header, []byte(" "), 2)
if len(split) == 2 {
http.Redirect(writer, request, rewriteURL(string(split[1]), request.URL), http.StatusSeeOther)
http.Redirect(writer, request, rewriteURL(string(split[1]), u), http.StatusSeeOther)
return
}
}
@ -234,6 +235,11 @@ func SetClientCertificate(domain string, certificate []byte, privateKey []byte)
return ErrInvalidCertificate
}
leafCert, err := x509.ParseCertificate(clientCert.Certificate[0])
if err == nil {
clientCert.Leaf = leafCert
}
clientCerts[domain] = clientCert
return nil
}