snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
267
server/cmd/mmctl/commands/init.go
Normal file
267
server/cmd/mmctl/commands/init.go
Normal file
@@ -0,0 +1,267 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
|
||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
|
||||
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
|
||||
)
|
||||
|
||||
const (
|
||||
shellCompletionMaxItems = 50 // Maximum number of items that will be loaded and shown in shell completion.
|
||||
shellCompleteTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
insecureSignatureAlgorithms = map[x509.SignatureAlgorithm]bool{
|
||||
x509.SHA1WithRSA: true,
|
||||
x509.DSAWithSHA1: true,
|
||||
x509.ECDSAWithSHA1: true,
|
||||
}
|
||||
expectedSocketMode = os.ModeSocket | 0600
|
||||
)
|
||||
|
||||
func CheckVersionMatch(version, serverVersion string) (bool, error) {
|
||||
mmctlVersionParsed, err := semver.NewVersion(version)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "Cannot parse version range %s", version)
|
||||
}
|
||||
|
||||
// Split and recombine the server version string
|
||||
parts := strings.Split(serverVersion, ".")
|
||||
if len(parts) < 3 {
|
||||
return false, fmt.Errorf("incorrect server version format: %s", serverVersion)
|
||||
}
|
||||
serverVersion = strings.Join(parts[:3], ".")
|
||||
|
||||
serverVersionParsed, err := semver.NewVersion(serverVersion)
|
||||
if err != nil {
|
||||
return false, errors.Wrapf(err, "Cannot parse version range %s", serverVersion)
|
||||
}
|
||||
|
||||
if serverVersionParsed.Major() != mmctlVersionParsed.Major() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if mmctlVersionParsed.Minor() > serverVersionParsed.Minor() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getClient(ctx context.Context, cmd *cobra.Command) (*model.Client4, string, bool, error) {
|
||||
useLocal := viper.GetBool("local")
|
||||
|
||||
if !useLocal {
|
||||
// Assume local mode if no server address is provided
|
||||
credentials, err := GetCurrentCredentials()
|
||||
if err != nil {
|
||||
cmd.PrintErrln("Warning: Unable to retrieve credentials, assuming --local mode")
|
||||
useLocal = true
|
||||
} else if credentials == nil {
|
||||
cmd.PrintErrln("Warning: No credentials found, assuming --local mode")
|
||||
useLocal = true
|
||||
}
|
||||
}
|
||||
|
||||
if useLocal {
|
||||
c, err := InitUnixClient(viper.GetString("local-socket-path"))
|
||||
if err != nil {
|
||||
return nil, "", true, err
|
||||
}
|
||||
printer.SetServerAddres("local instance")
|
||||
|
||||
return c, "", true, nil
|
||||
}
|
||||
|
||||
c, serverVersion, err := InitClient(ctx, viper.GetBool("insecure-sha1-intermediate"), viper.GetBool("insecure-tls-version"))
|
||||
if err != nil {
|
||||
return nil, "", false, err
|
||||
}
|
||||
|
||||
return c, serverVersion, false, nil
|
||||
}
|
||||
|
||||
func withClient(fn func(c client.Client, cmd *cobra.Command, args []string) error) func(cmd *cobra.Command, args []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.TODO()
|
||||
c, serverVersion, local, err := getClient(ctx, cmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create client: %w", err)
|
||||
}
|
||||
if local {
|
||||
return fn(c, cmd, args)
|
||||
}
|
||||
|
||||
if Version != "unspecified" { // unspecified version indicates that we are on dev mode.
|
||||
valid, err := CheckVersionMatch(Version, serverVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not check version mismatch: %w", err)
|
||||
}
|
||||
if !valid {
|
||||
if viper.GetBool("strict") {
|
||||
printer.PrintError("ERROR: server version " + serverVersion + " doesn't match with mmctl version " + Version + ". Strict flag is set, so the command will not be run")
|
||||
os.Exit(1)
|
||||
}
|
||||
if !viper.GetBool("suppress-warnings") {
|
||||
printer.PrintWarning("server version " + serverVersion + " doesn't match mmctl version " + Version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printer.SetServerAddres(c.APIURL)
|
||||
return fn(c, cmd, args)
|
||||
}
|
||||
}
|
||||
|
||||
func localOnlyPrecheck(cmd *cobra.Command, args []string) {
|
||||
local := viper.GetBool("local")
|
||||
if !local {
|
||||
fmt.Fprintln(os.Stderr, "This command can only be run in local mode")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func disableLocalPrecheck(cmd *cobra.Command, args []string) {
|
||||
local := viper.GetBool("local")
|
||||
if local {
|
||||
fmt.Fprintln(os.Stderr, "This command cannot be run in local mode")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func isValidChain(chain []*x509.Certificate) bool {
|
||||
// check all certs but the root one
|
||||
certs := chain[:len(chain)-1]
|
||||
|
||||
for _, cert := range certs {
|
||||
if _, ok := insecureSignatureAlgorithms[cert.SignatureAlgorithm]; ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func VerifyCertificates(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||
// loop over certificate chains
|
||||
if slices.ContainsFunc(verifiedChains, isValidChain) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("insecure algorithm found in the certificate chain. Use --insecure-sha1-intermediate flag to ignore. Aborting")
|
||||
}
|
||||
|
||||
func NewAPIv4Client(instanceURL string, allowInsecureSHA1, allowInsecureTLS bool) *model.Client4 {
|
||||
client := model.NewAPIv4Client(instanceURL)
|
||||
userAgent := fmt.Sprintf("mmctl/%s (%s)", Version, runtime.GOOS)
|
||||
client.HTTPHeader = map[string]string{"User-Agent": userAgent}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
if allowInsecureTLS {
|
||||
tlsConfig.MinVersion = tls.VersionTLS10
|
||||
}
|
||||
|
||||
if !allowInsecureSHA1 {
|
||||
tlsConfig.VerifyPeerCertificate = VerifyCertificates
|
||||
}
|
||||
|
||||
client.HTTPClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
},
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func InitClientWithUsernameAndPassword(ctx context.Context, username, password, instanceURL string, allowInsecureSHA1, allowInsecureTLS bool) (*model.Client4, string, error) {
|
||||
client := NewAPIv4Client(instanceURL, allowInsecureSHA1, allowInsecureTLS)
|
||||
|
||||
_, resp, err := client.Login(ctx, username, password)
|
||||
if err != nil {
|
||||
return nil, "", checkInsecureTLSError(err, allowInsecureTLS)
|
||||
}
|
||||
return client, resp.ServerVersion, nil
|
||||
}
|
||||
|
||||
func InitClientWithMFA(ctx context.Context, username, password, mfaToken, instanceURL string, allowInsecureSHA1, allowInsecureTLS bool) (*model.Client4, string, error) {
|
||||
client := NewAPIv4Client(instanceURL, allowInsecureSHA1, allowInsecureTLS)
|
||||
_, resp, err := client.LoginWithMFA(ctx, username, password, mfaToken)
|
||||
if err != nil {
|
||||
return nil, "", checkInsecureTLSError(err, allowInsecureTLS)
|
||||
}
|
||||
return client, resp.ServerVersion, nil
|
||||
}
|
||||
|
||||
func InitClientWithCredentials(ctx context.Context, credentials *Credentials, allowInsecureSHA1, allowInsecureTLS bool) (*model.Client4, string, error) {
|
||||
client := NewAPIv4Client(credentials.InstanceURL, allowInsecureSHA1, allowInsecureTLS)
|
||||
|
||||
client.AuthType = model.HeaderBearer
|
||||
client.AuthToken = credentials.AuthToken
|
||||
|
||||
_, resp, err := client.GetMe(ctx, "")
|
||||
if err != nil {
|
||||
return nil, "", checkInsecureTLSError(err, allowInsecureTLS)
|
||||
}
|
||||
|
||||
return client, resp.ServerVersion, nil
|
||||
}
|
||||
|
||||
func InitClient(ctx context.Context, allowInsecureSHA1, allowInsecureTLS bool) (*model.Client4, string, error) {
|
||||
credentials, err := GetCurrentCredentials()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return InitClientWithCredentials(ctx, credentials, allowInsecureSHA1, allowInsecureTLS)
|
||||
}
|
||||
|
||||
func InitWebSocketClient() (*model.WebSocketClient, error) {
|
||||
credentials, err := GetCurrentCredentials()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, appErr := model.NewWebSocketClient4(strings.Replace(credentials.InstanceURL, "http", "ws", 1), credentials.AuthToken)
|
||||
if appErr != nil {
|
||||
return nil, errors.Wrap(appErr, "unable to create the websockets connection")
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func InitUnixClient(socketPath string) (*model.Client4, error) {
|
||||
if err := checkValidSocket(socketPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return model.NewAPIv4SocketClient(socketPath), nil
|
||||
}
|
||||
|
||||
func checkInsecureTLSError(err error, allowInsecureTLS bool) error {
|
||||
if (strings.Contains(err.Error(), "tls: protocol version not supported") ||
|
||||
strings.Contains(err.Error(), "tls: server selected unsupported protocol version")) && !allowInsecureTLS {
|
||||
return errors.New("won't perform action through an insecure TLS connection. Please add --insecure-tls-version to bypass this check")
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user