/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ /* Package main parses command line flags and starts the s3proxy server. */ package main import ( "crypto/tls" "flag" "fmt" "log/slog" "net" "net/http" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/s3proxy/internal/router" ) const ( // defaultPort is the default port to listen on. defaultPort = 4433 // defaultIP is the default IP to listen on. defaultIP = "0.0.0.0" // defaultRegion is the default AWS region to use. defaultRegion = "eu-west-1" // defaultCertLocation is the default location of the TLS certificate. defaultCertLocation = "/etc/s3proxy/certs" // defaultLogLevel is the default log level. defaultLogLevel = 0 ) func main() { flags, err := parseFlags() if err != nil { panic(err) } // logLevel can be made a public variable so logging level can be changed dynamically. // TODO (derpsteb): enable once we are on go 1.21. // logLevel := new(slog.LevelVar) // handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: logLevel}) // logger := slog.New(handler) // logLevel.Set(flags.logLevel) logger := logger.NewJSONLogger(logger.VerbosityFromInt(flags.logLevel)) if flags.forwardMultipartReqs { logger.Warn("configured to forward multipart uploads, this may leak data to AWS") } if err := runServer(flags, logger); err != nil { panic(err) } } func runServer(flags cmdFlags, log *slog.Logger) error { log.With(slog.String("ip", flags.ip), slog.Int("port", defaultPort), slog.String("region", flags.region)).Info("listening") router, err := router.New(flags.region, flags.kmsEndpoint, flags.forwardMultipartReqs, log) if err != nil { return fmt.Errorf("creating router: %w", err) } server := http.Server{ Addr: fmt.Sprintf("%s:%d", flags.ip, defaultPort), Handler: http.HandlerFunc(router.Serve), // Disable HTTP/2. Serving HTTP/2 will cause some clients to use HTTP/2. // It seems like AWS S3 does not support HTTP/2. // Having HTTP/2 enabled will at least cause the aws-sdk-go V1 copy-object operation to fail. TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){}, } // i.e. if TLS is enabled. if !flags.noTLS { cert, err := tls.LoadX509KeyPair(flags.certLocation+"/s3proxy.crt", flags.certLocation+"/s3proxy.key") if err != nil { return fmt.Errorf("loading TLS certificate: %w", err) } server.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{cert}, } // TLSConfig is populated, so we can safely pass empty strings to ListenAndServeTLS. return server.ListenAndServeTLS("", "") } log.Warn("TLS is disabled") return server.ListenAndServe() } func parseFlags() (cmdFlags, error) { noTLS := flag.Bool("no-tls", false, "disable TLS and listen on port 80, otherwise listen on 443") ip := flag.String("ip", defaultIP, "ip to listen on") region := flag.String("region", defaultRegion, "AWS region in which target bucket is located") certLocation := flag.String("cert", defaultCertLocation, "location of TLS certificate") kmsEndpoint := flag.String("kms", "key-service.kube-system:9000", "endpoint of the KMS service to get key encryption keys from") forwardMultipartReqs := flag.Bool("allow-multipart", false, "forward multipart requests to the target bucket; beware: this may store unencrypted data on AWS. See the documentation for more information") level := flag.Int("level", defaultLogLevel, "log level") flag.Parse() netIP := net.ParseIP(*ip) if netIP == nil { return cmdFlags{}, fmt.Errorf("not a valid IPv4 address: %s", *ip) } // TODO(derpsteb): enable once we are on go 1.21. // logLevel := new(slog.Level) // if err := logLevel.UnmarshalText([]byte(*level)); err != nil { // return cmdFlags{}, fmt.Errorf("parsing log level: %w", err) // } return cmdFlags{ noTLS: *noTLS, ip: netIP.String(), region: *region, certLocation: *certLocation, kmsEndpoint: *kmsEndpoint, forwardMultipartReqs: *forwardMultipartReqs, logLevel: *level, }, nil } type cmdFlags struct { noTLS bool ip string region string certLocation string kmsEndpoint string forwardMultipartReqs bool // TODO(derpsteb): enable once we are on go 1.21. // logLevel slog.Level logLevel int }