diff options
| author | 2021-07-19 18:03:07 +0200 | |
|---|---|---|
| committer | 2021-07-19 18:03:07 +0200 | |
| commit | 677490bc4e8d61627bcab32ed801c10a27139f29 (patch) | |
| tree | 8ef634f6cf46886fa0baf83dc096824c05078096 | |
| parent | Static fileserver improvements, optional admin panel route (#100) (diff) | |
| download | gotosocial-677490bc4e8d61627bcab32ed801c10a27139f29.tar.xz | |
Db tls (#102)
* go mod tidy
* complete example config
* add tls support for db connection
* add certpool to tlsConfig
* add some lil docker scripts
| -rw-r--r-- | cmd/gotosocial/main.go | 12 | ||||
| -rwxr-xr-x | dockerbuild.sh | 3 | ||||
| -rwxr-xr-x | dockerpush.sh | 3 | ||||
| -rw-r--r-- | example/config.yaml | 161 | ||||
| -rw-r--r-- | go.mod | 1 | ||||
| -rw-r--r-- | go.sum | 4 | ||||
| -rw-r--r-- | internal/config/config.go | 64 | ||||
| -rw-r--r-- | internal/config/db.go | 33 | ||||
| -rw-r--r-- | internal/config/default.go | 14 | ||||
| -rw-r--r-- | internal/db/pg/pg.go | 52 | 
10 files changed, 302 insertions, 45 deletions
diff --git a/cmd/gotosocial/main.go b/cmd/gotosocial/main.go index 9729f7706..fde83e623 100644 --- a/cmd/gotosocial/main.go +++ b/cmd/gotosocial/main.go @@ -117,6 +117,18 @@ func main() {  				Value:   defaults.DbDatabase,  				EnvVars: []string{envNames.DbDatabase},  			}, +			&cli.StringFlag{ +				Name:    flagNames.DbTLSMode, +				Usage:   "Database tls mode", +				Value:   defaults.DBTlsMode, +				EnvVars: []string{envNames.DbTLSMode}, +			}, +			&cli.StringFlag{ +				Name:    flagNames.DbTLSCACert, +				Usage:   "Path to CA cert for db tls connection", +				Value:   defaults.DBTlsCACert, +				EnvVars: []string{envNames.DbTLSCACert}, +			},  			// TEMPLATE FLAGS  			&cli.StringFlag{ diff --git a/dockerbuild.sh b/dockerbuild.sh new file mode 100755 index 000000000..87893c65c --- /dev/null +++ b/dockerbuild.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t "superseriousbusiness/gotosocial:$(cat version)" . diff --git a/dockerpush.sh b/dockerpush.sh new file mode 100755 index 000000000..8377f8e4a --- /dev/null +++ b/dockerpush.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker push "superseriousbusiness/gotosocial:$(cat version)" diff --git a/example/config.yaml b/example/config.yaml index 81e4727e2..b26322812 100644 --- a/example/config.yaml +++ b/example/config.yaml @@ -18,7 +18,7 @@  ##### GENERAL CONFIG ######  ###########################  # String. Log level to use throughout the application. Must be lower-case. -# Options: ["debug","info","warn","error","fatal"] +# Options: ["trace","debug","info","warn","error","fatal"]  # Default: "info"  logLevel: "info" @@ -66,14 +66,29 @@ db:    # REQUIRED    # String. Password to use for the database connection    # Examples: ["password123","verysafepassword","postgres"] -  # Default: "" -  password: "" +  # Default: "postgres" +  password: "postgres"    # String. Name of the database to use within the provided database type.    # Examples: ["mydb","postgres","gotosocial"]    # Default: "postgres"    database: "postgres" +  # String. Disable, enable, or require SSL/TLS connection to the database. +  # If "disable" then no TLS connection will be attempted. +  # If "enable" then TLS will be tried, but the database certificate won't be checked (for self-signed certs). +  # If "require" then TLS will be required to make a connection, and a valid certificate must be presented. +  # Options: ["disable", "enable", "require"] +  # Default: "disable" +  tlsMode: "disable" + +  # String. Path to a CA certificate on the host machine for db certificate validation. +  # If this is left empty, just the host certificates will be used. +  # If filled in, the certificate will be loaded and added to host certificates. +  # Examples: ["/path/to/some/cert.crt"] +  # Default: "" +  tlsCACert: "" +  ###############################  ##### WEB TEMPLATE CONFIG #####  ############################### @@ -84,6 +99,11 @@ template:    # Default: "./web/template/"    baseDir: "./web/template/" +  # String. Directory from which gotosocial will attempt to serve static web assets (images, scripts). +  # Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"] +  # Default: "./web/assets/" +  assetBaseDir: "./web/assets/" +  ###########################  ##### ACCOUNTS CONFIG #####  ########################### @@ -93,7 +113,142 @@ accounts:    # Options: [true, false]    # Default: true    openRegistration: true +    # Bool. Do sign up requests require approval from an admin/moderator before an account can sign in/use the server?    # Options: [true, false]    # Default: true    requireApproval: true + +  # Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)? +  # Options: [true, false] +  # Default: true +  reasonRequired: true + +######################## +##### MEDIA CONFIG ##### +######################## +# Config pertaining to user media uploads (videos, image, image descriptions). +media: +  # Int. Maximum allowed image upload size in bytes. +  # Examples: [2097152, 10485760] +  # Default: 2097152 -- aka 2MB +  maxImageSize: 2097152 + +  # Int. Maximum allowed video upload size in bytes. +  # Examples: [2097152, 10485760] +  # Default: 10485760 -- aka 10MB +  maxVideoSize: 10485760 + +  # Int. Minimum amount of characters required as an image or video description. +  # Examples: [500, 1000, 1500] +  # Default: 0 (not required) +  minDescriptionChars: 0 + +  # Int. Maximum amount of characters permitted in an image or video description. +  # Examples: [500, 1000, 1500] +  # Default: 500 +  maxDescriptionChars: 500 + +########################## +##### STORAGE CONFIG ##### +########################## +# Config pertaining to storage of user-created uploads (videos, images, etc). +storage: +  # String. Type of storage backend to use. +  # Examples: ["local", "s3"] +  # Default: "local" (storage on local disk) +  # NOTE: s3 storage is not yet supported! +  backend: "local" + +  # String. Directory to use as a base path for storing files. +  # Make sure whatever user/group gotosocial is running as has permission to access +  # this directly, and create new subdirectories and files with in. +  # Examples: ["/home/gotosocial/storage", "/opt/gotosocial/datastorage"] +  # Default: "/gotosocial/storage" +  basePath: "/gotosocial/storage" + +  # String. Protocol to use for serving stored files. +  # It's very unlikely that you'll need to change this ever, but there might be edge cases. +  # Examples: ["http", "https"] +  serveProtocol: "https" + +  # String. Host for serving stored files. +  # If you're using local storage, this should be THE SAME as the value you've set for Host, above. +  # It should only be a different value if you're serving stored files from a host +  # other than the one your instance is running on. +  # Examples: ["localhost", "example.org"] +  # Default: "localhost" -- you should absolutely change this. +  serveHost: "localhost" + +  # String. Base path for serving stored files. This will be added to serveHost and serveProtocol +  # to form the prefix url of your stored files. Eg., https://example.org/fileserver/..... +  # It's unlikely that you will need to change this. +  # Examples: ["/fileserver", "/media"] +  # Default: "/fileserver" +  serveBasePath: "/fileserver" + +########################### +##### STATUSES CONFIG ##### +########################### +# Config pertaining to the creation of statuses/posts, and permitted limits. +statuses: +  # Int. Maximum amount of characters permitted for a new status. +  # Note that going way higher than the default might break federation. +  # Examples: [140, 500, 5000] +  # Default: 5000 +  maxChars: 5000 + +  # Int. Maximum amount of characters allowed in the CW/subject header of a status. +  # Note that going way higher than the default might break federation. +  # Examples: [100, 200] +  # Default: 100 +  cwMaxChars: 100 + +  # Int. Maximum amount of options to permit when creating a new poll. +  # Note that going way higher than the default might break federation. +  # Examples: [4, 6, 10] +  # Default: 6 +  pollMaxOptions: 6 + +  # Int. Maximum amount of characters to permit per poll option when creating a new poll. +  # Note that going way higher than the default might break federation. +  # Examples: [50, 100, 150] +  # Default: 50 +  pollOptionMaxChars: 50 + +  # Int. Maximum amount of media files that can be attached to a new status. +  # Note that going way higher than the default might break federation. +  # Examples: [4, 6, 10] +  # Default: 6 +  maxMediaFiles: 6 + +############################## +##### LETSENCRYPT CONFIG ##### +############################## +# Config pertaining to the automatic acquisition and use of LetsEncrypt HTTPS certificates. +letsEncrypt: +  # Bool. Whether or not letsencrypt should be enabled for the server. +  # If true, the server will serve on port 443 (https) and obtain letsencrypt +  # certificates automatically. +  # If false, the server will serve on port 8080 (http), and the rest of the settings +  # here will be ignored. +  # You should only change this if you want to serve GoToSocial behind a reverse proxy +  # like Traefik, HAProxy, or Nginx. +  # Options: [true, false] +  # Default: true  +  enabled: true + +  # String. Directory in which to store LetsEncrypt certificates. +  # It is a good move to make this a sub-path within your storage directory, as it makes +  # backup easier, but you might wish to move them elsewhere if they're also accessed by other services. +  # In any case, make sure GoToSocial has permissions to write to / read from this directory. +  # Examples: ["/home/gotosocial/storage/certs", "/acmecerts"] +  # Default: "/gotosocial/storage/certs" +  certDir: "/gotosocial/storage/certs" + +  # String. Email address to use when registering LetsEncrypt certs. +  # Most likely, this will be the email address of the instance administrator. +  # LetsEncrypt will send notifications about expiring certificates etc to this address. +  # Examples: ["admin@example.org"] +  # Default: "" +  emailAddress: "" @@ -15,7 +15,6 @@ require (  	github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e // indirect  	github.com/gin-contrib/cors v1.3.1  	github.com/gin-contrib/sessions v0.0.3 -	github.com/gin-contrib/static v0.0.1  	github.com/gin-gonic/gin v1.7.2  	github.com/go-errors/errors v1.4.0 // indirect  	github.com/go-fed/activity v1.0.1-0.20210426194615-e0de0863dcc1 @@ -69,10 +69,7 @@ github.com/gin-contrib/sessions v0.0.3 h1:PoBXki+44XdJdlgDqDrY5nDVe3Wk7wDV/UCOuL  github.com/gin-contrib/sessions v0.0.3/go.mod h1:8C/J6cad3Il1mWYYgtw0w+hqasmpvy25mPkXdOgeB9I=  github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=  github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U= -github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=  github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=  github.com/gin-gonic/gin v1.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=  github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=  github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= @@ -101,7 +98,6 @@ github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTM  github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=  github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=  github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=  github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=  github.com/go-playground/validator/v10 v10.6.1 h1:W6TRDXt4WcWp4c4nf/G+6BkGdhiIo0k417gfr+V6u4I=  github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= diff --git a/internal/config/config.go b/internal/config/config.go index 28bbc8542..323b7de81 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -165,6 +165,14 @@ func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error {  		c.DBConfig.Database = f.String(fn.DbDatabase)  	} +	if c.DBConfig.TLSMode == DBTLSModeUnset || f.IsSet(fn.DbTLSMode) { +		c.DBConfig.TLSMode = DBTLSMode(f.String(fn.DbTLSMode)) +	} + +	if c.DBConfig.TLSCACert == "" || f.IsSet(fn.DbTLSCACert) { +		c.DBConfig.TLSCACert = f.String(fn.DbTLSCACert) +	} +  	// template flags  	if c.TemplateConfig.BaseDir == "" || f.IsSet(fn.TemplateBaseDir) {  		c.TemplateConfig.BaseDir = f.String(fn.TemplateBaseDir) @@ -284,12 +292,14 @@ type Flags struct {  	Host            string  	Protocol        string -	DbType     string -	DbAddress  string -	DbPort     string -	DbUser     string -	DbPassword string -	DbDatabase string +	DbType      string +	DbAddress   string +	DbPort      string +	DbUser      string +	DbPassword  string +	DbDatabase  string +	DbTLSMode   string +	DbTLSCACert string  	TemplateBaseDir string  	AssetBaseDir    string @@ -329,12 +339,14 @@ type Defaults struct {  	Protocol        string  	SoftwareVersion string -	DbType     string -	DbAddress  string -	DbPort     int -	DbUser     string -	DbPassword string -	DbDatabase string +	DbType      string +	DbAddress   string +	DbPort      int +	DbUser      string +	DbPassword  string +	DbDatabase  string +	DBTlsMode   string +	DBTlsCACert string  	TemplateBaseDir string  	AssetBaseDir    string @@ -375,12 +387,14 @@ func GetFlagNames() Flags {  		Host:            "host",  		Protocol:        "protocol", -		DbType:     "db-type", -		DbAddress:  "db-address", -		DbPort:     "db-port", -		DbUser:     "db-user", -		DbPassword: "db-password", -		DbDatabase: "db-database", +		DbType:      "db-type", +		DbAddress:   "db-address", +		DbPort:      "db-port", +		DbUser:      "db-user", +		DbPassword:  "db-password", +		DbDatabase:  "db-database", +		DbTLSMode:   "db-tls-mode", +		DbTLSCACert: "db-tls-ca-cert",  		TemplateBaseDir: "template-basedir",  		AssetBaseDir:    "asset-basedir", @@ -422,12 +436,14 @@ func GetEnvNames() Flags {  		Host:            "GTS_HOST",  		Protocol:        "GTS_PROTOCOL", -		DbType:     "GTS_DB_TYPE", -		DbAddress:  "GTS_DB_ADDRESS", -		DbPort:     "GTS_DB_PORT", -		DbUser:     "GTS_DB_USER", -		DbPassword: "GTS_DB_PASSWORD", -		DbDatabase: "GTS_DB_DATABASE", +		DbType:      "GTS_DB_TYPE", +		DbAddress:   "GTS_DB_ADDRESS", +		DbPort:      "GTS_DB_PORT", +		DbUser:      "GTS_DB_USER", +		DbPassword:  "GTS_DB_PASSWORD", +		DbDatabase:  "GTS_DB_DATABASE", +		DbTLSMode:   "GTS_DB_TLS_MODE", +		DbTLSCACert: "GTS_DB_CA_CERT",  		TemplateBaseDir: "GTS_TEMPLATE_BASEDIR",  		AssetBaseDir:    "GTS_ASSET_BASEDIR", diff --git a/internal/config/db.go b/internal/config/db.go index fbde6fe82..7ea71a8b6 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -20,11 +20,30 @@ package config  // DBConfig provides configuration options for the database connection  type DBConfig struct { -	Type            string `yaml:"type"` -	Address         string `yaml:"address"` -	Port            int    `yaml:"port"` -	User            string `yaml:"user"` -	Password        string `yaml:"password"` -	Database        string `yaml:"database"` -	ApplicationName string `yaml:"applicationName"` +	Type            string    `yaml:"type"` +	Address         string    `yaml:"address"` +	Port            int       `yaml:"port"` +	User            string    `yaml:"user"` +	Password        string    `yaml:"password"` +	Database        string    `yaml:"database"` +	ApplicationName string    `yaml:"applicationName"` +	TLSMode         DBTLSMode `yaml:"tlsMode"` +	TLSCACert       string    `yaml:"tlsCACert"`  } + +// DBTLSMode describes a mode of connecting to a database with or without TLS. +type DBTLSMode string + +// DBTLSModeDisable does not attempt to make a TLS connection to the database. +var DBTLSModeDisable DBTLSMode = "disable" + +// DBTLSModeEnable attempts to make a TLS connection to the database, but doesn't fail if +// the certificate passed by the database isn't verified. +var DBTLSModeEnable DBTLSMode = "enable" + +// DBTLSModeRequire attempts to make a TLS connection to the database, and requires +// that the certificate presented by the database is valid. +var DBTLSModeRequire DBTLSMode = "require" + +// DBTLSModeUnset means that the TLS mode has not been set. +var DBTLSModeUnset DBTLSMode = "" diff --git a/internal/config/default.go b/internal/config/default.go index 40df4c57e..7a030beb5 100644 --- a/internal/config/default.go +++ b/internal/config/default.go @@ -120,12 +120,14 @@ func GetDefaults() Defaults {  		Host:            "",  		Protocol:        "https", -		DbType:     "postgres", -		DbAddress:  "localhost", -		DbPort:     5432, -		DbUser:     "postgres", -		DbPassword: "postgres", -		DbDatabase: "postgres", +		DbType:      "postgres", +		DbAddress:   "localhost", +		DbPort:      5432, +		DbUser:      "postgres", +		DbPassword:  "postgres", +		DbDatabase:  "postgres", +		DBTlsMode:   "disable", +		DBTlsCACert: "",  		TemplateBaseDir: "./web/template/",  		AssetBaseDir:    "./web/assets/", diff --git a/internal/db/pg/pg.go b/internal/db/pg/pg.go index ad75cef15..5301f0410 100644 --- a/internal/db/pg/pg.go +++ b/internal/db/pg/pg.go @@ -22,10 +22,14 @@ import (  	"context"  	"crypto/rand"  	"crypto/rsa" +	"crypto/tls" +	"crypto/x509" +	"encoding/pem"  	"errors"  	"fmt"  	"net"  	"net/mail" +	"os"  	"strings"  	"time" @@ -133,6 +137,53 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) {  		return nil, errors.New("no database set")  	} +	var tlsConfig *tls.Config +	switch c.DBConfig.TLSMode { +	case config.DBTLSModeDisable, config.DBTLSModeUnset: +		break // nothing to do +	case config.DBTLSModeEnable: +		tlsConfig = &tls.Config{ +			InsecureSkipVerify: true, +		} +	case config.DBTLSModeRequire: +		tlsConfig = &tls.Config{ +			InsecureSkipVerify: false, +		} +	} + +	if tlsConfig != nil && c.DBConfig.TLSCACert != "" { +		// load the system cert pool first -- we'll append the given CA cert to this +		certPool, err := x509.SystemCertPool() +		if err != nil { +			return nil, fmt.Errorf("error fetching system CA cert pool: %s", err) +		} + +		// open the file itself and make sure there's something in it +		caCertBytes, err := os.ReadFile(c.DBConfig.TLSCACert) +		if err != nil { +			return nil, fmt.Errorf("error opening CA certificate at %s: %s", c.DBConfig.TLSCACert, err) +		} +		if len(caCertBytes) == 0 { +			return nil, fmt.Errorf("ca cert at %s was empty", c.DBConfig.TLSCACert) +		} + +		// make sure we have a PEM block +		caPem, _ := pem.Decode(caCertBytes) +		if caPem == nil { +			return nil, fmt.Errorf("could not parse cert at %s into PEM", c.DBConfig.TLSCACert) +		} + +		// parse the PEM block into the certificate +		caCert, err := x509.ParseCertificate(caPem.Bytes) +		if err != nil { +			return nil, fmt.Errorf("could not parse cert at %s into x509 certificate: %s", c.DBConfig.TLSCACert, err) +		} + +		// we're happy, add it to the existing pool and then use this pool in our tls config +		certPool.AddCert(caCert) +		tlsConfig.RootCAs = certPool +	} +  	// We can rely on the pg library we're using to set  	// sensible defaults for everything we don't set here.  	options := &pg.Options{ @@ -141,6 +192,7 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) {  		Password:        c.DBConfig.Password,  		Database:        c.DBConfig.Database,  		ApplicationName: c.ApplicationName, +		TLSConfig:       tlsConfig,  	}  	return options, nil  | 
