Simplify server certificate and known hosts management (#1807)

This commit is contained in:
jannfis 2019-07-12 01:00:47 +02:00 committed by Alex Collins
parent 641e344c7f
commit 9cf744f435
60 changed files with 6122 additions and 520 deletions

View file

@ -107,7 +107,6 @@ RUN groupadd -g 999 argocd && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY hack/ssh_known_hosts /etc/ssh/ssh_known_hosts
COPY hack/git-ask-pass.sh /usr/local/bin/git-ask-pass.sh
COPY --from=builder /usr/local/bin/ks /usr/local/bin/ks
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
@ -116,6 +115,13 @@ COPY --from=builder /usr/local/bin/kustomize1 /usr/local/bin/kustomize1
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
COPY --from=builder /usr/local/bin/aws-iam-authenticator /usr/local/bin/aws-iam-authenticator
# support for mounting configuration from a configmap
RUN mkdir -p /app/config/ssh && \
touch /app/config/ssh/ssh_known_hosts && \
ln -s /app/config/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts
RUN mkdir -p /app/config/tls
# workaround ksonnet issue https://github.com/ksonnet/ksonnet/issues/298
ENV USER=argocd

2
Gopkg.lock generated
View file

@ -1652,9 +1652,11 @@
"gopkg.in/src-d/go-git.v4/config",
"gopkg.in/src-d/go-git.v4/plumbing",
"gopkg.in/src-d/go-git.v4/plumbing/transport",
"gopkg.in/src-d/go-git.v4/plumbing/transport/client",
"gopkg.in/src-d/go-git.v4/plumbing/transport/http",
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh",
"gopkg.in/src-d/go-git.v4/storage/memory",
"gopkg.in/src-d/go-git.v4/utils/ioutil",
"gopkg.in/yaml.v2",
"k8s.io/api/apps/v1",
"k8s.io/api/batch/v1",

View file

@ -7,6 +7,7 @@
# p, <user/group>, <resource>, <action>, <object>
p, role:readonly, applications, get, */*, allow
p, role:readonly, certificates, get, *, allow
p, role:readonly, clusters, get, *, allow
p, role:readonly, repositories, get, *, allow
p, role:readonly, projects, get, *, allow
@ -16,6 +17,9 @@ p, role:admin, applications, update, */*, allow
p, role:admin, applications, delete, */*, allow
p, role:admin, applications, sync, */*, allow
p, role:admin, applications, override, */*, allow
p, role:admin, certificates, create, *, allow
p, role:admin, certificates, update, *, allow
p, role:admin, certificates, delete, *, allow
p, role:admin, clusters, create, *, allow
p, role:admin, clusters, update, *, allow
p, role:admin, clusters, delete, *, allow

1 # Built-in policy which defines two roles: role:readonly and role:admin,
7 # p, <user/group>, <resource>, <action>, <object>
8 p, role:readonly, applications, get, */*, allow
9 p, role:readonly, clusters, get, *, allow p, role:readonly, certificates, get, *, allow
10 p, role:readonly, clusters, get, *, allow
11 p, role:readonly, repositories, get, *, allow
12 p, role:readonly, projects, get, *, allow
13 p, role:admin, applications, create, */*, allow
17 p, role:admin, applications, override, */*, allow
18 p, role:admin, clusters, create, *, allow p, role:admin, certificates, create, *, allow
19 p, role:admin, clusters, update, *, allow p, role:admin, certificates, update, *, allow
20 p, role:admin, certificates, delete, *, allow
21 p, role:admin, clusters, create, *, allow
22 p, role:admin, clusters, update, *, allow
23 p, role:admin, clusters, delete, *, allow
24 p, role:admin, repositories, create, *, allow
25 p, role:admin, repositories, update, *, allow

View file

@ -49,7 +49,7 @@
"ApplicationService"
],
"summary": "List returns list of applications",
"operationId": "ListMixin5",
"operationId": "ListMixin1",
"parameters": [
{
"type": "string",
@ -89,7 +89,7 @@
"ApplicationService"
],
"summary": "Create creates an application",
"operationId": "CreateMixin5",
"operationId": "CreateMixin1",
"parameters": [
{
"name": "body",
@ -116,7 +116,7 @@
"ApplicationService"
],
"summary": "Update updates an application",
"operationId": "UpdateMixin5",
"operationId": "UpdateMixin1",
"parameters": [
{
"type": "string",
@ -197,7 +197,7 @@
"ApplicationService"
],
"summary": "Get returns an application by name",
"operationId": "GetMixin5",
"operationId": "Get",
"parameters": [
{
"type": "string",
@ -238,7 +238,7 @@
"ApplicationService"
],
"summary": "Delete deletes an application",
"operationId": "DeleteMixin5",
"operationId": "DeleteMixin1",
"parameters": [
{
"type": "string",
@ -769,13 +769,89 @@
}
}
},
"/api/v1/certificates": {
"get": {
"tags": [
"CertificateService"
],
"summary": "List all available certificates",
"operationId": "ListMixin2",
"parameters": [
{
"type": "string",
"description": "A file-glob pattern (not regular expression) the host name has to match.",
"name": "hostNamePattern",
"in": "query"
},
{
"type": "string",
"description": "The type of the certificate to match (ssh or https).",
"name": "certType",
"in": "query"
},
{
"type": "string",
"description": "The sub type of the certificate to match (protocol dependent, usually only used for ssh certs).",
"name": "certSubType",
"in": "query"
}
],
"responses": {
"200": {
"description": "(empty)",
"schema": {
"$ref": "#/definitions/v1alpha1RepositoryCertificateList"
}
}
}
},
"post": {
"tags": [
"CertificateService"
],
"summary": "Creates the requested certificates on the server",
"operationId": "CreateMixin2",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/v1alpha1RepositoryCertificateList"
}
}
],
"responses": {
"200": {
"description": "(empty)",
"schema": {
"$ref": "#/definitions/v1alpha1RepositoryCertificateList"
}
}
}
},
"delete": {
"tags": [
"CertificateService"
],
"operationId": "DeleteMixin2",
"responses": {
"200": {
"description": "(empty)",
"schema": {
"$ref": "#/definitions/v1alpha1RepositoryCertificateList"
}
}
}
}
},
"/api/v1/clusters": {
"get": {
"tags": [
"ClusterService"
],
"summary": "List returns list of clusters",
"operationId": "List",
"operationId": "ListMixin3",
"parameters": [
{
"type": "string",
@ -797,7 +873,7 @@
"ClusterService"
],
"summary": "Create creates a cluster",
"operationId": "Create",
"operationId": "CreateMixin3",
"parameters": [
{
"name": "body",
@ -824,7 +900,7 @@
"ClusterService"
],
"summary": "Update updates a cluster",
"operationId": "Update",
"operationId": "UpdateMixin3",
"parameters": [
{
"type": "string",
@ -857,7 +933,7 @@
"ClusterService"
],
"summary": "Get returns a cluster by server address",
"operationId": "GetMixin1",
"operationId": "GetMixin3",
"parameters": [
{
"type": "string",
@ -880,7 +956,7 @@
"ClusterService"
],
"summary": "Delete deletes a cluster",
"operationId": "Delete",
"operationId": "DeleteMixin3",
"parameters": [
{
"type": "string",
@ -930,7 +1006,7 @@
"ProjectService"
],
"summary": "List returns list of projects",
"operationId": "ListMixin3",
"operationId": "ListMixin4",
"parameters": [
{
"type": "string",
@ -952,7 +1028,7 @@
"ProjectService"
],
"summary": "Create a new project.",
"operationId": "CreateMixin3",
"operationId": "CreateMixin4",
"parameters": [
{
"name": "body",
@ -979,7 +1055,7 @@
"ProjectService"
],
"summary": "Get returns a project by name",
"operationId": "GetMixin3",
"operationId": "GetMixin4",
"parameters": [
{
"type": "string",
@ -1002,7 +1078,7 @@
"ProjectService"
],
"summary": "Delete deletes a project",
"operationId": "DeleteMixin3",
"operationId": "DeleteMixin4",
"parameters": [
{
"type": "string",
@ -1052,7 +1128,7 @@
"ProjectService"
],
"summary": "Update updates a project",
"operationId": "UpdateMixin3",
"operationId": "UpdateMixin4",
"parameters": [
{
"type": "string",
@ -1162,7 +1238,7 @@
"RepositoryService"
],
"summary": "List returns list of repos",
"operationId": "ListMixin2",
"operationId": "List",
"parameters": [
{
"type": "string",
@ -1184,7 +1260,7 @@
"RepositoryService"
],
"summary": "Create creates a repo",
"operationId": "CreateMixin2",
"operationId": "Create",
"parameters": [
{
"name": "body",
@ -1211,7 +1287,7 @@
"RepositoryService"
],
"summary": "Update updates a repo",
"operationId": "UpdateMixin2",
"operationId": "Update",
"parameters": [
{
"type": "string",
@ -1244,7 +1320,7 @@
"RepositoryService"
],
"summary": "Delete deletes a repo",
"operationId": "DeleteMixin2",
"operationId": "Delete",
"parameters": [
{
"type": "string",
@ -1342,13 +1418,46 @@
}
}
},
"/api/v1/repositories/{repo}/validate": {
"post": {
"tags": [
"RepositoryService"
],
"summary": "ValidateAccess validates access to a repository with given parameters",
"operationId": "ValidateAccess",
"parameters": [
{
"type": "string",
"name": "repo",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "(empty)",
"schema": {
"$ref": "#/definitions/repositoryRepoResponse"
}
}
}
}
},
"/api/v1/session": {
"post": {
"tags": [
"SessionService"
],
"summary": "Create a new JWT for authentication and set a cookie if using HTTP.",
"operationId": "CreateMixin7",
"operationId": "CreateMixin8",
"parameters": [
{
"name": "body",
@ -1373,7 +1482,7 @@
"SessionService"
],
"summary": "Delete an existing JWT cookie if using HTTP.",
"operationId": "DeleteMixin7",
"operationId": "DeleteMixin8",
"responses": {
"200": {
"description": "(empty)",
@ -1390,7 +1499,7 @@
"SettingsService"
],
"summary": "Get returns Argo CD settings",
"operationId": "Get",
"operationId": "GetMixin5",
"responses": {
"200": {
"description": "(empty)",
@ -3145,21 +3254,74 @@
"connectionState": {
"$ref": "#/definitions/v1alpha1ConnectionState"
},
"insecure": {
"type": "boolean",
"format": "boolean",
"title": "Whether the repo is insecure"
},
"insecureIgnoreHostKey": {
"type": "boolean",
"format": "boolean"
"format": "boolean",
"title": "InsecureIgnoreHostKey should not be used anymore, Insecure is favoured"
},
"password": {
"type": "string"
"type": "string",
"title": "Password for authenticating at the repo server"
},
"repo": {
"type": "string"
"type": "string",
"title": "URL of the repo"
},
"sshPrivateKey": {
"type": "string"
"type": "string",
"title": "SSH private key data for authenticating at the repo server"
},
"username": {
"type": "string"
"type": "string",
"title": "Username for authenticating at the repo server"
}
}
},
"v1alpha1RepositoryCertificate": {
"type": "object",
"title": "A RepositoryCertificate is either SSH known hosts entry or TLS certificate",
"properties": {
"certdata": {
"type": "string",
"format": "byte",
"title": "Actual certificate data, protocol dependent"
},
"certfingerprint": {
"type": "string",
"title": "Certificate fingerprint"
},
"cipher": {
"type": "string",
"title": "The sub type of the cert, i.e. \"ssh-rsa\""
},
"servername": {
"type": "string",
"title": "Name of the server the certificate is intended for"
},
"type": {
"type": "string",
"title": "Type of certificate - currently \"https\" or \"ssh\""
}
}
},
"v1alpha1RepositoryCertificateList": {
"type": "object",
"title": "RepositoryCertificateList is a collection of RepositoryCertificates",
"properties": {
"items": {
"type": "array",
"title": "List of certificates to be processed",
"items": {
"$ref": "#/definitions/v1alpha1RepositoryCertificate"
}
},
"metadata": {
"$ref": "#/definitions/v1ListMeta"
}
}
},

305
cmd/argocd/commands/cert.go Normal file
View file

@ -0,0 +1,305 @@
package commands
import (
"context"
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"github.com/spf13/cobra"
"github.com/argoproj/argo-cd/errors"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util"
certutil "github.com/argoproj/argo-cd/util/cert"
"crypto/x509"
)
// NewCertCommand returns a new instance of an `argocd repo` command
func NewCertCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var command = &cobra.Command{
Use: "cert",
Short: "Manage repository certificates and SSH known hosts entries",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
os.Exit(1)
},
}
command.AddCommand(NewCertAddSSHCommand(clientOpts))
command.AddCommand(NewCertAddTLSCommand(clientOpts))
command.AddCommand(NewCertListCommand(clientOpts))
command.AddCommand(NewCertRemoveCommand(clientOpts))
return command
}
func NewCertAddTLSCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
fromFile string
upsert bool
)
var command = &cobra.Command{
Use: "add-tls SERVERNAME",
Short: "Add TLS certificate data for connecting to repository server SERVERNAME",
Run: func(c *cobra.Command, args []string) {
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
defer util.Close(conn)
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
var certificateArray []string
var err error
if fromFile != "" {
fmt.Printf("Reading TLS certificate data in PEM format from '%s'\n", fromFile)
certificateArray, err = certutil.ParseTLSCertificatesFromPath(fromFile)
} else {
fmt.Println("Enter TLS certificate data in PEM format. Press CTRL-D when finished.")
certificateArray, err = certutil.ParseTLSCertificatesFromStream(os.Stdin)
}
errors.CheckError(err)
certificateList := make([]appsv1.RepositoryCertificate, 0)
subjectMap := make(map[string]*x509.Certificate)
for _, entry := range certificateArray {
// We want to make sure to only send valid certificate data to the
// server, so we decode the certificate into X509 structure before
// further processing it.
x509cert, err := certutil.DecodePEMCertificateToX509(entry)
errors.CheckError(err)
// TODO: We need a better way to detect duplicates sent in the stream,
// maybe by using fingerprints? For now, no two certs with the same
// subject may be sent.
if subjectMap[x509cert.Subject.String()] != nil {
fmt.Printf("ERROR: Cert with subject '%s' already seen in the input stream.\n", x509cert.Subject.String())
continue
} else {
subjectMap[x509cert.Subject.String()] = x509cert
}
}
serverName := args[0]
if len(certificateArray) > 0 {
certificateList = append(certificateList, appsv1.RepositoryCertificate{
ServerName: serverName,
CertType: "https",
CertData: []byte(strings.Join(certificateArray, "\n")),
})
certificates, err := certIf.Create(context.Background(), &certificatepkg.RepositoryCertificateCreateRequest{
Certificates: &appsv1.RepositoryCertificateList{
Items: certificateList,
},
Upsert: upsert,
})
errors.CheckError(err)
fmt.Printf("Created entry with %d PEM certificates for repository server %s\n", len(certificates.Items), serverName)
} else {
fmt.Printf("No valid certificates have been detected in the stream.\n")
}
},
}
command.Flags().StringVar(&fromFile, "from", "", "read TLS certificate data from file (default is to read from stdin)")
command.Flags().BoolVar(&upsert, "upsert", false, "Replace existing TLS certificate if certificate is different in input")
return command
}
// NewCertAddCommand returns a new instance of an `argocd cert add` command
func NewCertAddSSHCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
fromFile string
batchProcess bool
upsert bool
certificates []appsv1.RepositoryCertificate
)
var command = &cobra.Command{
Use: "add-ssh --batch",
Short: "Add SSH known host entries for repository servers",
Run: func(c *cobra.Command, args []string) {
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
defer util.Close(conn)
var sshKnownHostsLists []string
var err error
// --batch is a flag, but it is mandatory for now.
if batchProcess {
if fromFile != "" {
fmt.Printf("Reading SSH known hosts entries from file '%s'\n", fromFile)
sshKnownHostsLists, err = certutil.ParseSSHKnownHostsFromPath(fromFile)
} else {
fmt.Println("Enter SSH known hosts entries, one per line. Press CTRL-D when finished.")
sshKnownHostsLists, err = certutil.ParseSSHKnownHostsFromStream(os.Stdin)
}
} else {
err = fmt.Errorf("You need to specify --batch or specify --help for usage instructions")
}
errors.CheckError(err)
if len(sshKnownHostsLists) == 0 {
errors.CheckError(fmt.Errorf("No valid SSH known hosts data found."))
}
for _, knownHostsEntry := range sshKnownHostsLists {
hostname, certSubType, certData, err := certutil.TokenizeSSHKnownHostsEntry(knownHostsEntry)
errors.CheckError(err)
_, _, err = certutil.KnownHostsLineToPublicKey(knownHostsEntry)
errors.CheckError(err)
certificate := appsv1.RepositoryCertificate{
ServerName: hostname,
CertType: "ssh",
CertSubType: certSubType,
CertData: certData,
}
certificates = append(certificates, certificate)
}
certList := &appsv1.RepositoryCertificateList{Items: certificates}
response, err := certIf.Create(context.Background(), &certificatepkg.RepositoryCertificateCreateRequest{
Certificates: certList,
Upsert: upsert,
})
errors.CheckError(err)
fmt.Printf("Successfully created %d SSH known host entries\n", len(response.Items))
},
}
command.Flags().StringVar(&fromFile, "from", "", "Read SSH known hosts data from file (default is to read from stdin)")
command.Flags().BoolVar(&batchProcess, "batch", false, "Perform batch processing by reading in SSH known hosts data (mandatory flag)")
command.Flags().BoolVar(&upsert, "upsert", false, "Replace existing SSH server public host keys if key is different in input")
return command
}
// NewCertRemoveCommand returns a new instance of an `argocd cert rm` command
func NewCertRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
removeAllCerts bool
certType string
certSubType string
certQuery certificatepkg.RepositoryCertificateQuery
)
var command = &cobra.Command{
Use: "rm REPOSERVER",
Short: "Remove certificate of TYPE for REPOSERVER",
Run: func(c *cobra.Command, args []string) {
if len(args) < 1 && !removeAllCerts {
c.HelpFunc()(c, args)
os.Exit(1)
}
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
defer util.Close(conn)
if removeAllCerts {
certQuery = certificatepkg.RepositoryCertificateQuery{
HostNamePattern: "*",
CertType: "*",
CertSubType: "*",
}
} else {
certQuery = certificatepkg.RepositoryCertificateQuery{
HostNamePattern: args[0],
CertType: certType,
CertSubType: certSubType,
}
}
removed, err := certIf.Delete(context.Background(), &certQuery)
errors.CheckError(err)
if len(removed.Items) > 0 {
for _, cert := range removed.Items {
fmt.Printf("Removed cert for '%s' of type '%s' (subtype '%s')\n", cert.ServerName, cert.CertType, cert.CertSubType)
}
} else {
fmt.Println("No certificates were removed (none matched the given patterns)")
}
},
}
command.Flags().BoolVar(&removeAllCerts, "remove-all", false, "Remove all configured certificates of all types from server (DANGER: use with care!)")
command.Flags().StringVar(&certType, "cert-type", "", "Only remove certs of given type (ssh, https)")
command.Flags().StringVar(&certSubType, "cert-sub-type", "", "Only remove certs of given sub-type (only for ssh)")
return command
}
// NewCertListCommand returns a new instance of an `argocd cert rm` command
func NewCertListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
certType string
hostNamePattern string
sortOrder string
)
var command = &cobra.Command{
Use: "list",
Short: "List configured certificates",
Run: func(c *cobra.Command, args []string) {
if certType != "" {
switch certType {
case "ssh":
case "https":
default:
fmt.Println("cert-type must be either ssh or https")
os.Exit(1)
}
}
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
defer util.Close(conn)
certificates, err := certIf.List(context.Background(), &certificatepkg.RepositoryCertificateQuery{HostNamePattern: hostNamePattern, CertType: certType})
errors.CheckError(err)
printCertTable(certificates.Items, sortOrder)
},
}
command.Flags().StringVar(&sortOrder, "sort", "", "set display sort order, valid: 'hostname', 'type'")
command.Flags().StringVar(&certType, "cert-type", "", "only list certificates of given type, valid: 'ssh','https'")
command.Flags().StringVar(&hostNamePattern, "hostname-pattern", "", "only list certificates for hosts matching given glob-pattern")
return command
}
// Print table of certificate info
func printCertTable(certs []appsv1.RepositoryCertificate, sortOrder string) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "HOSTNAME\tTYPE\tSUBTYPE\tFINGERPRINT/SUBJECT\n")
if sortOrder == "hostname" || sortOrder == "" {
sort.Slice(certs, func(i, j int) bool {
return certs[i].ServerName < certs[j].ServerName
})
} else if sortOrder == "type" {
sort.Slice(certs, func(i, j int) bool {
return certs[i].CertType < certs[j].CertType
})
}
for _, c := range certs {
if c.CertType == "ssh" {
_, pubKey, err := certutil.TokenizedDataToPublicKey(c.ServerName, c.CertSubType, string(c.CertData))
errors.CheckError(err)
fmt.Fprintf(w, "%s\t%s\t%s\tSHA256:%s\n", c.ServerName, c.CertType, c.CertSubType, certutil.SSHFingerprintSHA256(pubKey))
} else if c.CertType == "https" {
x509Data, err := certutil.DecodePEMCertificateToX509(string(c.CertData))
var subject string
keyType := "-?-"
if err != nil {
subject = err.Error()
} else {
subject = x509Data.Subject.String()
keyType = x509Data.PublicKeyAlgorithm.String()
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", c.ServerName, c.CertType, strings.ToLower(keyType), subject)
}
}
_ = w.Flush()
}

View file

@ -16,7 +16,6 @@ import (
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/git"
)
// NewRepoCommand returns a new instance of an `argocd repo` command
@ -39,10 +38,11 @@ func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
// NewRepoAddCommand returns a new instance of an `argocd repo add` command
func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
repo appsv1.Repository
upsert bool
sshPrivateKeyPath string
insecureIgnoreHostKey bool
repo appsv1.Repository
upsert bool
sshPrivateKeyPath string
insecureIgnoreHostKey bool
insecureSkipServerVerification bool
)
var command = &cobra.Command{
Use: "add REPO",
@ -60,24 +60,32 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
}
repo.SSHPrivateKey = string(keyData)
}
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
repo.InsecureIgnoreHostKey = insecureIgnoreHostKey
// First test the repo *without* username/password. This gives us a hint on whether this
// is a private repo.
// NOTE: it is important not to run git commands to test git credentials on the user's
// system since it may mess with their git credential store (e.g. osx keychain).
// See issue #315
err := git.TestRepo(repo.Repo, "", "", repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
if err != nil {
if yes, _ := git.IsSSHURL(repo.Repo); yes {
// If we failed using git SSH credentials, then the repo is automatically bad
log.Fatal(err)
}
// If we can't test the repo, it's probably private. Prompt for credentials and
// let the server test it.
repo.Username, repo.Password = cli.PromptCredentials(repo.Username, repo.Password)
}
repo.Insecure = insecureSkipServerVerification
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoClientOrDie()
defer util.Close(conn)
// If the user set a username, but didn't supply password via --password,
// then we prompt for it
if repo.Username != "" && repo.Password == "" {
repo.Password = cli.PromptPassword(repo.Password)
}
// We let the server check access to the repository before adding it. If
// it is a private repo, but we cannot access with with the credentials
// that were supplied, we bail out.
repoAccessReq := repositorypkg.RepoAccessQuery{
Repo: repo.Repo,
Username: repo.Username,
Password: repo.Password,
SshPrivateKey: repo.SSHPrivateKey,
Insecure: (repo.InsecureIgnoreHostKey || repo.Insecure),
}
_, err := repoIf.ValidateAccess(context.Background(), &repoAccessReq)
errors.CheckError(err)
repoCreateReq := repositorypkg.RepoCreateRequest{
Repo: &repo,
Upsert: upsert,
@ -90,7 +98,8 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
command.Flags().StringVar(&repo.Username, "username", "", "username to the repository")
command.Flags().StringVar(&repo.Password, "password", "", "password to the repository")
command.Flags().StringVar(&sshPrivateKeyPath, "ssh-private-key-path", "", "path to the private ssh key (e.g. ~/.ssh/id_rsa)")
command.Flags().BoolVar(&insecureIgnoreHostKey, "insecure-ignore-host-key", false, "disables SSH strict host key checking")
command.Flags().BoolVar(&insecureIgnoreHostKey, "insecure-ignore-host-key", false, "disables SSH strict host key checking (deprecated, use --insecure-skip-server-validation instead)")
command.Flags().BoolVar(&insecureSkipServerVerification, "insecure-skip-server-verification", false, "disables server certificate and host key checks")
command.Flags().BoolVar(&upsert, "upsert", false, "Override an existing repository with the same name even if the spec differs")
return command
}
@ -119,9 +128,15 @@ func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
// Print table of repo info
func printRepoTable(repos []appsv1.Repository) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "REPO\tUSER\tSTATUS\tMESSAGE\n")
fmt.Fprintf(w, "REPO\tINSECURE\tUSER\tSTATUS\tMESSAGE\n")
for _, r := range repos {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", r.Repo, r.Username, r.ConnectionState.Status, r.ConnectionState.Message)
var username string
if r.Username == "" {
username = "-"
} else {
username = r.Username
}
fmt.Fprintf(w, "%s\t%v\t%s\t%s\t%s\n", r.Repo, r.Insecure, username, r.ConnectionState.Status, r.ConnectionState.Message)
}
_ = w.Flush()
}

View file

@ -47,6 +47,7 @@ func NewCommand() *cobra.Command {
command.AddCommand(NewProjectCommand(&clientOpts))
command.AddCommand(NewAccountCommand(&clientOpts))
command.AddCommand(NewLogoutCommand(&clientOpts))
command.AddCommand(NewCertCommand(&clientOpts))
defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath()
errors.CheckError(err)

View file

@ -15,6 +15,10 @@ const (
ArgoCDConfigMapName = "argocd-cm"
ArgoCDSecretName = "argocd-secret"
ArgoCDRBACConfigMapName = "argocd-rbac-cm"
// Contains SSH known hosts data for connecting repositories. Will get mounted as volume to pods
ArgoCDKnownHostsConfigMapName = "argocd-ssh-known-hosts-cm"
// Contains TLS certificate data for connecting repositories. Will get mounted as volume to pods
ArgoCDTLSCertsConfigMapName = "argocd-tls-certs-cm"
)
// Default system namespace

View file

@ -64,6 +64,9 @@ func newFakeController(data *fakeData) *ApplicationController {
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-cm",
Namespace: test.FakeArgoCDNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: nil,
}

View file

@ -43,6 +43,54 @@ argocd repo add git@github.com:argoproj/argocd-example-apps.git --ssh-private-ke
## Self-signed & Untrusted TLS Certificates
> v1.2 or higher
If you are connecting a repository on a HTTPS server using a self-signed certificate, or a certificate signed by a custom Certificate Authority (CA) which are not known to ArgoCD, the repository will not be added due to security reasons. This is indicated by an error message such as `x509: certificate signed by unknown authority`.
1. You can let ArgoCD connect the repository in an insecure way, without verifying the server's certificate at all. This can be accomplished by using the `--insecure-repository` flag when adding the repository with the `argocd` CLI utility. However, this should be done only for non-production setups, as it imposes a serious security issue through possible man-in-the-middle attacks.
2. You can let ArgoCD use a custom certificate for the verification of the server's certificate using the `cert add-tls` command of the `argocd` CLI utility. This is the recommended method and suitable for production use. In order to do so, you will need the server's certificate, or the certificate of the CA used to sign the server's certificate, in PEM format.
!!! note
For invalid server certificates, such as those without matching server name, or those that are expired, adding a CA certificate will not help. In this case, your only option will be to use the `--insecure-repository` flag to connect the repository. You are strongly urged to use a valid certificate on the repository server, or to urge the server's administrator to replace the faulty certificate with a valid one.
Example for adding a HTTPS repository to ArgoCD without verifying the server's certificate (**Caution:** This is **not** recommended for production use):
```bash
argocd repo add --insecure-repository https://git.example.com/test-repo
```
Example for adding a CA certificate contained in file `~/myca-cert.pem` to properly verify the repository server:
```bash
argocd cert add-tls git.example.com --from ~/myca-cert.pem
argocd repo add https://git.example.com/test-repo
```
You can also add more than one PEM for a server by concatenating them into the input stream. This might be useful if the repository server is about to replace the server certificate, possibly with one signed by a different CA. This way, you can have the old (current) as well as the new (future) certificate co-existing. If you already have the old certificate configured, use the `--upsert` flag and add the old and the new one in a single run:
```bash
cat cert1.pem cert2.pem | argocd cert add-tls git.example.com --upsert
```
!!! note
You can add multiple TLS certificates for a single server by either using a file containing multiple PEM certificates, or by using the `--append` flag to the `cert add-tls` command of the CLI.
!!! note
To replace an existing certificate for a server, use the `--upsert` flag to the `cert add-tls` CLI command.
!!! note
TLS certificates are configured on a per-server, not on a per-repository basis. If you connect multiple repositories from the same server, you only have to configure the certificates once for this server.
!!! note
It can take up to a couple of minutes until the changes performed by the `argocd cert` command are propagated across your cluster, depending on your Kubernetes setup.
You can also manage TLS certificates in a declarative, self-managed ArgoCD setup. All TLS certificates are stored in the ConfigMap object `argocd-tls-cert-cm`.
Managing TLS certificates via the web UI is currently not possible.
> Before v1.2
We do not currently have first-class support for this. See [#1513](https://github.com/argoproj/argo-cd/issues/1513).
As a work-around, you can customize your Argo CD image. See [#1344](https://github.com/argoproj/argo-cd/issues/1344#issuecomment-479811810)
@ -50,6 +98,35 @@ As a work-around, you can customize your Argo CD image. See [#1344](https://gith
## Unknown SSH Hosts
If you are using a privately hosted Git service over SSH, then you have the following options:
> v1.2 or later
1. You can let ArgoCD connect the repository in an insecure way, without verifying the server's SSH host key at all. This can be accomplished by using the `--insecure-repository` flag when adding the repository with the `argocd` CLI utility. However, this should be done only for non-production setups, as it imposes a serious security issue through possible man-in-the-middle attacks.
2. You can make the server's SSH public key known to ArgoCD by using the `cert add-ssh` command of the `argocd` CLI utility. This is the recommended method and suitable for production use. In order to do so, you will need the server's SSH public host key, in the `known_hosts` format understood by `ssh`. You can get the server's public SSH host key e.g. by using the `ssh-keyscan` utility.
Example for adding all available SSH public host keys for a server to ArgoCD:
```bash
ssh-keyscan server.example.com | argocd cert add-ssh --batch
```
Example for importing an existing `known_hosts` file to ArgoCD:
```bash
argocd cert add-ssh --batch --from /etc/ssh/ssh_known_hosts
```
!!! note
It can take up to a couple of minutes until the changes performed by the `argocd cert` command are propagated across your cluster, depending on your Kubernetes setup.
You can also manage SSH known hosts entries in a declarative, self-managed ArgoCD setup. All SSH public host keys are stored in the ConfigMap object `argocd-ssh-known-hosts-cm`.
Managing SSH public host keys via the web UI is currently not possible.
> Before v1.2
(1) You can customize the Argo CD Docker image by adding the host's SSH public key to `/etc/ssh/ssh_known_hosts`. Additional entries to this file can be generated using the `ssh-keyscan` utility (e.g. `ssh-keyscan your-private-git-server.com`. For more information see [example](https://github.com/argoproj/argo-cd/tree/master/examples/known-hosts) which demonstrates how `/etc/ssh/ssh_known_hosts` can be customized.
@ -71,5 +148,5 @@ argocd repo add git@github.com:argoproj/argocd-example-apps.git --ssh-private-ke
## Declarative Configuration
See [declarative setup](../operator-manual/declarative-setup#Repositories)
See [declarative setup](../../operator-manual/declarative-setup#Repositories)

View file

@ -121,7 +121,7 @@ clean_swagger() {
/usr/bin/find "${SWAGGER_ROOT}" -name '*.swagger.json' -delete
}
collect_swagger server 26
collect_swagger server 27
clean_swagger server
clean_swagger reposerver
clean_swagger controller

View file

@ -0,0 +1,16 @@
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-ssh-known-hosts-cm
app.kubernetes.io/part-of: argocd
name: argocd-ssh-known-hosts-cm
data:
ssh_known_hosts: |
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H

View file

@ -0,0 +1,8 @@
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-tls-certs-cm
app.kubernetes.io/part-of: argocd
name: argocd-tls-certs-cm
data:

View file

@ -4,4 +4,6 @@ kind: Kustomization
resources:
- argocd-cm.yaml
- argocd-secret.yaml
- argocd-rbac-cm.yaml
- argocd-rbac-cm.yaml
- argocd-ssh-known-hosts-cm.yaml
- argocd-tls-certs-cm.yaml

View file

@ -37,3 +37,15 @@ spec:
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: ssh-known-hosts
mountPath: /app/config/ssh
- name: tls-certs
mountPath: /app/config/tls
volumes:
- name: ssh-known-hosts
configMap:
name: argocd-ssh-known-hosts-cm
- name: tls-certs
configMap:
name: argocd-tls-certs-cm

View file

@ -32,6 +32,10 @@ spec:
volumeMounts:
- mountPath: /shared
name: static-files
- name: ssh-known-hosts
mountPath: /app/config/ssh
- name: tls-certs
mountPath: /app/config/tls
ports:
- containerPort: 8080
- containerPort: 8083
@ -50,3 +54,9 @@ spec:
volumes:
- emptyDir: {}
name: static-files
- name: ssh-known-hosts
configMap:
name: argocd-ssh-known-hosts-cm
- name: tls-certs
configMap:
name: argocd-tls-certs-cm

View file

@ -2720,6 +2720,32 @@ metadata:
name: argocd-redis-ha-probes
---
apiVersion: v1
data:
ssh_known_hosts: |
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-ssh-known-hosts-cm
app.kubernetes.io/part-of: argocd
name: argocd-ssh-known-hosts-cm
---
apiVersion: v1
data: null
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-tls-certs-cm
app.kubernetes.io/part-of: argocd
name: argocd-tls-certs-cm
---
apiVersion: v1
kind: Secret
metadata:
labels:
@ -3086,6 +3112,18 @@ spec:
periodSeconds: 10
tcpSocket:
port: 8081
volumeMounts:
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
volumes:
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs
---
apiVersion: apps/v1
kind: Deployment
@ -3153,6 +3191,10 @@ spec:
volumeMounts:
- mountPath: /shared
name: static-files
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
initContainers:
- command:
- cp
@ -3169,6 +3211,12 @@ spec:
volumes:
- emptyDir: {}
name: static-files
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs
---
apiVersion: apps/v1
kind: StatefulSet

View file

@ -2635,6 +2635,32 @@ metadata:
name: argocd-redis-ha-probes
---
apiVersion: v1
data:
ssh_known_hosts: |
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-ssh-known-hosts-cm
app.kubernetes.io/part-of: argocd
name: argocd-ssh-known-hosts-cm
---
apiVersion: v1
data: null
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-tls-certs-cm
app.kubernetes.io/part-of: argocd
name: argocd-tls-certs-cm
---
apiVersion: v1
kind: Secret
metadata:
labels:
@ -3001,6 +3027,18 @@ spec:
periodSeconds: 10
tcpSocket:
port: 8081
volumeMounts:
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
volumes:
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs
---
apiVersion: apps/v1
kind: Deployment
@ -3068,6 +3106,10 @@ spec:
volumeMounts:
- mountPath: /shared
name: static-files
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
initContainers:
- command:
- cp
@ -3084,6 +3126,12 @@ spec:
volumes:
- emptyDir: {}
name: static-files
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs
---
apiVersion: apps/v1
kind: StatefulSet

View file

@ -2578,6 +2578,32 @@ metadata:
name: argocd-rbac-cm
---
apiVersion: v1
data:
ssh_known_hosts: |
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-ssh-known-hosts-cm
app.kubernetes.io/part-of: argocd
name: argocd-ssh-known-hosts-cm
---
apiVersion: v1
data: null
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-tls-certs-cm
app.kubernetes.io/part-of: argocd
name: argocd-tls-certs-cm
---
apiVersion: v1
kind: Secret
metadata:
labels:
@ -2858,6 +2884,18 @@ spec:
periodSeconds: 10
tcpSocket:
port: 8081
volumeMounts:
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
volumes:
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs
---
apiVersion: apps/v1
kind: Deployment
@ -2902,6 +2940,10 @@ spec:
volumeMounts:
- mountPath: /shared
name: static-files
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
initContainers:
- command:
- cp
@ -2918,3 +2960,9 @@ spec:
volumes:
- emptyDir: {}
name: static-files
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs

View file

@ -2493,6 +2493,32 @@ metadata:
name: argocd-rbac-cm
---
apiVersion: v1
data:
ssh_known_hosts: |
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-ssh-known-hosts-cm
app.kubernetes.io/part-of: argocd
name: argocd-ssh-known-hosts-cm
---
apiVersion: v1
data: null
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-tls-certs-cm
app.kubernetes.io/part-of: argocd
name: argocd-tls-certs-cm
---
apiVersion: v1
kind: Secret
metadata:
labels:
@ -2773,6 +2799,18 @@ spec:
periodSeconds: 10
tcpSocket:
port: 8081
volumeMounts:
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
volumes:
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs
---
apiVersion: apps/v1
kind: Deployment
@ -2817,6 +2855,10 @@ spec:
volumeMounts:
- mountPath: /shared
name: static-files
- mountPath: /app/config/ssh
name: ssh-known-hosts
- mountPath: /app/config/tls
name: tls-certs
initContainers:
- command:
- cp
@ -2833,3 +2875,9 @@ spec:
volumes:
- emptyDir: {}
name: static-files
- configMap:
name: argocd-ssh-known-hosts-cm
name: ssh-known-hosts
- configMap:
name: argocd-tls-certs-cm
name: tls-certs

View file

@ -27,6 +27,7 @@ import (
"github.com/argoproj/argo-cd/common"
accountpkg "github.com/argoproj/argo-cd/pkg/apiclient/account"
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
@ -58,6 +59,8 @@ type Client interface {
OIDCConfig(context.Context, *settingspkg.Settings) (*oauth2.Config, *oidc.Provider, error)
NewRepoClient() (io.Closer, repositorypkg.RepositoryServiceClient, error)
NewRepoClientOrDie() (io.Closer, repositorypkg.RepositoryServiceClient)
NewCertClient() (io.Closer, certificatepkg.CertificateServiceClient, error)
NewCertClientOrDie() (io.Closer, certificatepkg.CertificateServiceClient)
NewClusterClient() (io.Closer, clusterpkg.ClusterServiceClient, error)
NewClusterClientOrDie() (io.Closer, clusterpkg.ClusterServiceClient)
NewApplicationClient() (io.Closer, applicationpkg.ApplicationServiceClient, error)
@ -437,6 +440,23 @@ func (c *client) NewRepoClientOrDie() (io.Closer, repositorypkg.RepositoryServic
return conn, repoIf
}
func (c *client) NewCertClient() (io.Closer, certificatepkg.CertificateServiceClient, error) {
conn, closer, err := c.newConn()
if err != nil {
return nil, nil, err
}
certIf := certificatepkg.NewCertificateServiceClient(conn)
return closer, certIf, nil
}
func (c *client) NewCertClientOrDie() (io.Closer, certificatepkg.CertificateServiceClient) {
conn, certIf, err := c.NewCertClient()
if err != nil {
log.Fatalf("Failed to establish connection to %s: %v", c.ServerAddr, err)
}
return conn, certIf
}
func (c *client) NewClusterClient() (io.Closer, clusterpkg.ClusterServiceClient, error) {
conn, closer, err := c.newConn()
if err != nil {

View file

@ -0,0 +1,949 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: server/certificate/certificate.proto
package certificate // import "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
/*
Certificate Service
Certificate Service API performs CRUD actions against repository certificate
resources.
*/
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
import v1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
import _ "github.com/gogo/protobuf/gogoproto"
import _ "google.golang.org/genproto/googleapis/api/annotations"
import context "golang.org/x/net/context"
import grpc "google.golang.org/grpc"
import io "io"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
// Message to query the server for configured repository certificates
type RepositoryCertificateQuery struct {
// A file-glob pattern (not regular expression) the host name has to match
HostNamePattern string `protobuf:"bytes,1,opt,name=hostNamePattern,proto3" json:"hostNamePattern,omitempty"`
// The type of the certificate to match (ssh or https)
CertType string `protobuf:"bytes,2,opt,name=certType,proto3" json:"certType,omitempty"`
// The sub type of the certificate to match (protocol dependent, usually only used for ssh certs)
CertSubType string `protobuf:"bytes,3,opt,name=certSubType,proto3" json:"certSubType,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RepositoryCertificateQuery) Reset() { *m = RepositoryCertificateQuery{} }
func (m *RepositoryCertificateQuery) String() string { return proto.CompactTextString(m) }
func (*RepositoryCertificateQuery) ProtoMessage() {}
func (*RepositoryCertificateQuery) Descriptor() ([]byte, []int) {
return fileDescriptor_certificate_7cceab803f50bba4, []int{0}
}
func (m *RepositoryCertificateQuery) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RepositoryCertificateQuery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RepositoryCertificateQuery.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *RepositoryCertificateQuery) XXX_Merge(src proto.Message) {
xxx_messageInfo_RepositoryCertificateQuery.Merge(dst, src)
}
func (m *RepositoryCertificateQuery) XXX_Size() int {
return m.Size()
}
func (m *RepositoryCertificateQuery) XXX_DiscardUnknown() {
xxx_messageInfo_RepositoryCertificateQuery.DiscardUnknown(m)
}
var xxx_messageInfo_RepositoryCertificateQuery proto.InternalMessageInfo
func (m *RepositoryCertificateQuery) GetHostNamePattern() string {
if m != nil {
return m.HostNamePattern
}
return ""
}
func (m *RepositoryCertificateQuery) GetCertType() string {
if m != nil {
return m.CertType
}
return ""
}
func (m *RepositoryCertificateQuery) GetCertSubType() string {
if m != nil {
return m.CertSubType
}
return ""
}
// Request to create a set of certificates
type RepositoryCertificateCreateRequest struct {
// List of certificates to be created
Certificates *v1alpha1.RepositoryCertificateList `protobuf:"bytes,1,opt,name=certificates" json:"certificates,omitempty"`
// Whether to upsert already existing certificates
Upsert bool `protobuf:"varint,2,opt,name=upsert,proto3" json:"upsert,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RepositoryCertificateCreateRequest) Reset() { *m = RepositoryCertificateCreateRequest{} }
func (m *RepositoryCertificateCreateRequest) String() string { return proto.CompactTextString(m) }
func (*RepositoryCertificateCreateRequest) ProtoMessage() {}
func (*RepositoryCertificateCreateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_certificate_7cceab803f50bba4, []int{1}
}
func (m *RepositoryCertificateCreateRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RepositoryCertificateCreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RepositoryCertificateCreateRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *RepositoryCertificateCreateRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RepositoryCertificateCreateRequest.Merge(dst, src)
}
func (m *RepositoryCertificateCreateRequest) XXX_Size() int {
return m.Size()
}
func (m *RepositoryCertificateCreateRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RepositoryCertificateCreateRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RepositoryCertificateCreateRequest proto.InternalMessageInfo
func (m *RepositoryCertificateCreateRequest) GetCertificates() *v1alpha1.RepositoryCertificateList {
if m != nil {
return m.Certificates
}
return nil
}
func (m *RepositoryCertificateCreateRequest) GetUpsert() bool {
if m != nil {
return m.Upsert
}
return false
}
type RepositoryCertificateResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RepositoryCertificateResponse) Reset() { *m = RepositoryCertificateResponse{} }
func (m *RepositoryCertificateResponse) String() string { return proto.CompactTextString(m) }
func (*RepositoryCertificateResponse) ProtoMessage() {}
func (*RepositoryCertificateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_certificate_7cceab803f50bba4, []int{2}
}
func (m *RepositoryCertificateResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RepositoryCertificateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RepositoryCertificateResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *RepositoryCertificateResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RepositoryCertificateResponse.Merge(dst, src)
}
func (m *RepositoryCertificateResponse) XXX_Size() int {
return m.Size()
}
func (m *RepositoryCertificateResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RepositoryCertificateResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RepositoryCertificateResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*RepositoryCertificateQuery)(nil), "certificate.RepositoryCertificateQuery")
proto.RegisterType((*RepositoryCertificateCreateRequest)(nil), "certificate.RepositoryCertificateCreateRequest")
proto.RegisterType((*RepositoryCertificateResponse)(nil), "certificate.RepositoryCertificateResponse")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for CertificateService service
type CertificateServiceClient interface {
// List all available certificates
List(ctx context.Context, in *RepositoryCertificateQuery, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error)
// Creates the requested certificates on the server
Create(ctx context.Context, in *RepositoryCertificateCreateRequest, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error)
Delete(ctx context.Context, in *RepositoryCertificateQuery, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error)
}
type certificateServiceClient struct {
cc *grpc.ClientConn
}
func NewCertificateServiceClient(cc *grpc.ClientConn) CertificateServiceClient {
return &certificateServiceClient{cc}
}
func (c *certificateServiceClient) List(ctx context.Context, in *RepositoryCertificateQuery, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error) {
out := new(v1alpha1.RepositoryCertificateList)
err := c.cc.Invoke(ctx, "/certificate.CertificateService/List", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *certificateServiceClient) Create(ctx context.Context, in *RepositoryCertificateCreateRequest, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error) {
out := new(v1alpha1.RepositoryCertificateList)
err := c.cc.Invoke(ctx, "/certificate.CertificateService/Create", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *certificateServiceClient) Delete(ctx context.Context, in *RepositoryCertificateQuery, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error) {
out := new(v1alpha1.RepositoryCertificateList)
err := c.cc.Invoke(ctx, "/certificate.CertificateService/Delete", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for CertificateService service
type CertificateServiceServer interface {
// List all available certificates
List(context.Context, *RepositoryCertificateQuery) (*v1alpha1.RepositoryCertificateList, error)
// Creates the requested certificates on the server
Create(context.Context, *RepositoryCertificateCreateRequest) (*v1alpha1.RepositoryCertificateList, error)
Delete(context.Context, *RepositoryCertificateQuery) (*v1alpha1.RepositoryCertificateList, error)
}
func RegisterCertificateServiceServer(s *grpc.Server, srv CertificateServiceServer) {
s.RegisterService(&_CertificateService_serviceDesc, srv)
}
func _CertificateService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RepositoryCertificateQuery)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CertificateServiceServer).List(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/certificate.CertificateService/List",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CertificateServiceServer).List(ctx, req.(*RepositoryCertificateQuery))
}
return interceptor(ctx, in, info, handler)
}
func _CertificateService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RepositoryCertificateCreateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CertificateServiceServer).Create(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/certificate.CertificateService/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CertificateServiceServer).Create(ctx, req.(*RepositoryCertificateCreateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _CertificateService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RepositoryCertificateQuery)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CertificateServiceServer).Delete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/certificate.CertificateService/Delete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CertificateServiceServer).Delete(ctx, req.(*RepositoryCertificateQuery))
}
return interceptor(ctx, in, info, handler)
}
var _CertificateService_serviceDesc = grpc.ServiceDesc{
ServiceName: "certificate.CertificateService",
HandlerType: (*CertificateServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "List",
Handler: _CertificateService_List_Handler,
},
{
MethodName: "Create",
Handler: _CertificateService_Create_Handler,
},
{
MethodName: "Delete",
Handler: _CertificateService_Delete_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "server/certificate/certificate.proto",
}
func (m *RepositoryCertificateQuery) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RepositoryCertificateQuery) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.HostNamePattern) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintCertificate(dAtA, i, uint64(len(m.HostNamePattern)))
i += copy(dAtA[i:], m.HostNamePattern)
}
if len(m.CertType) > 0 {
dAtA[i] = 0x12
i++
i = encodeVarintCertificate(dAtA, i, uint64(len(m.CertType)))
i += copy(dAtA[i:], m.CertType)
}
if len(m.CertSubType) > 0 {
dAtA[i] = 0x1a
i++
i = encodeVarintCertificate(dAtA, i, uint64(len(m.CertSubType)))
i += copy(dAtA[i:], m.CertSubType)
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *RepositoryCertificateCreateRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RepositoryCertificateCreateRequest) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Certificates != nil {
dAtA[i] = 0xa
i++
i = encodeVarintCertificate(dAtA, i, uint64(m.Certificates.Size()))
n1, err := m.Certificates.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n1
}
if m.Upsert {
dAtA[i] = 0x10
i++
if m.Upsert {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i++
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *RepositoryCertificateResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RepositoryCertificateResponse) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func encodeVarintCertificate(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return offset + 1
}
func (m *RepositoryCertificateQuery) Size() (n int) {
var l int
_ = l
l = len(m.HostNamePattern)
if l > 0 {
n += 1 + l + sovCertificate(uint64(l))
}
l = len(m.CertType)
if l > 0 {
n += 1 + l + sovCertificate(uint64(l))
}
l = len(m.CertSubType)
if l > 0 {
n += 1 + l + sovCertificate(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RepositoryCertificateCreateRequest) Size() (n int) {
var l int
_ = l
if m.Certificates != nil {
l = m.Certificates.Size()
n += 1 + l + sovCertificate(uint64(l))
}
if m.Upsert {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RepositoryCertificateResponse) Size() (n int) {
var l int
_ = l
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovCertificate(x uint64) (n int) {
for {
n++
x >>= 7
if x == 0 {
break
}
}
return n
}
func sozCertificate(x uint64) (n int) {
return sovCertificate(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *RepositoryCertificateQuery) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RepositoryCertificateQuery: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RepositoryCertificateQuery: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field HostNamePattern", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthCertificate
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.HostNamePattern = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CertType", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthCertificate
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.CertType = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field CertSubType", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthCertificate
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.CertSubType = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipCertificate(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthCertificate
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RepositoryCertificateCreateRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RepositoryCertificateCreateRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RepositoryCertificateCreateRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Certificates", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthCertificate
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Certificates == nil {
m.Certificates = &v1alpha1.RepositoryCertificateList{}
}
if err := m.Certificates.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Upsert", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.Upsert = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipCertificate(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthCertificate
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RepositoryCertificateResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCertificate
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RepositoryCertificateResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RepositoryCertificateResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipCertificate(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthCertificate
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipCertificate(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowCertificate
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowCertificate
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
return iNdEx, nil
case 1:
iNdEx += 8
return iNdEx, nil
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowCertificate
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
iNdEx += length
if length < 0 {
return 0, ErrInvalidLengthCertificate
}
return iNdEx, nil
case 3:
for {
var innerWire uint64
var start int = iNdEx
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowCertificate
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
innerWire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
innerWireType := int(innerWire & 0x7)
if innerWireType == 4 {
break
}
next, err := skipCertificate(dAtA[start:])
if err != nil {
return 0, err
}
iNdEx = start + next
}
return iNdEx, nil
case 4:
return iNdEx, nil
case 5:
iNdEx += 4
return iNdEx, nil
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
}
panic("unreachable")
}
var (
ErrInvalidLengthCertificate = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowCertificate = fmt.Errorf("proto: integer overflow")
)
func init() {
proto.RegisterFile("server/certificate/certificate.proto", fileDescriptor_certificate_7cceab803f50bba4)
}
var fileDescriptor_certificate_7cceab803f50bba4 = []byte{
// 448 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x53, 0x41, 0x6b, 0x14, 0x31,
0x14, 0x26, 0xb5, 0x2c, 0x35, 0x15, 0x84, 0x50, 0x4a, 0x19, 0xea, 0x5a, 0x86, 0x82, 0xa5, 0x60,
0xc2, 0x56, 0xbc, 0x78, 0x74, 0xbd, 0x08, 0x22, 0x3a, 0xed, 0xc9, 0x8b, 0x64, 0x67, 0x9f, 0xd9,
0xd8, 0x69, 0x12, 0x93, 0x37, 0x83, 0x7b, 0x15, 0xff, 0x81, 0x7f, 0x40, 0x10, 0xff, 0x82, 0x17,
0xff, 0x80, 0x47, 0xc1, 0x3f, 0x20, 0x8b, 0x3f, 0x44, 0x26, 0xdb, 0xda, 0x8c, 0x8c, 0xe8, 0x65,
0xc1, 0xdb, 0xcb, 0xf7, 0x92, 0xf7, 0xbe, 0xef, 0x7b, 0x79, 0x74, 0x3f, 0x80, 0x6f, 0xc0, 0x8b,
0x12, 0x3c, 0xea, 0x17, 0xba, 0x94, 0x08, 0x69, 0xcc, 0x9d, 0xb7, 0x68, 0xd9, 0x66, 0x02, 0x65,
0x5b, 0xca, 0x2a, 0x1b, 0x71, 0xd1, 0x46, 0xcb, 0x2b, 0xd9, 0xae, 0xb2, 0x56, 0x55, 0x20, 0xa4,
0xd3, 0x42, 0x1a, 0x63, 0x51, 0xa2, 0xb6, 0x26, 0x9c, 0x67, 0x1f, 0x2a, 0x8d, 0xb3, 0x7a, 0xc2,
0x4b, 0x7b, 0x26, 0xa4, 0x8f, 0xcf, 0x5f, 0xc6, 0xe0, 0x76, 0x39, 0x15, 0xee, 0x54, 0xb5, 0xcf,
0x82, 0x90, 0xce, 0x55, 0x6d, 0x0f, 0x6d, 0x8d, 0x68, 0x46, 0xb2, 0x72, 0x33, 0x39, 0x12, 0x0a,
0x0c, 0x78, 0x89, 0x30, 0x5d, 0x96, 0xca, 0xdf, 0x12, 0x9a, 0x15, 0xe0, 0x6c, 0xd0, 0x68, 0xfd,
0x7c, 0x7c, 0x49, 0xec, 0x69, 0x0d, 0x7e, 0xce, 0x0e, 0xe8, 0xf5, 0x99, 0x0d, 0xf8, 0x58, 0x9e,
0xc1, 0x13, 0x89, 0x08, 0xde, 0xec, 0x90, 0x3d, 0x72, 0x70, 0xb5, 0xf8, 0x1d, 0x66, 0x19, 0xdd,
0x68, 0x65, 0x9d, 0xcc, 0x1d, 0xec, 0xac, 0xc5, 0x2b, 0xbf, 0xce, 0x6c, 0x8f, 0x46, 0xc9, 0xc7,
0xf5, 0x24, 0xa6, 0xaf, 0xc4, 0x74, 0x0a, 0xe5, 0x9f, 0x08, 0xcd, 0x7b, 0x69, 0x8c, 0x3d, 0x48,
0x84, 0x02, 0x5e, 0xd5, 0x10, 0x90, 0xbd, 0xa6, 0xd7, 0x12, 0xef, 0x42, 0xe4, 0xb2, 0x79, 0x74,
0xc2, 0x2f, 0xfd, 0xe0, 0x17, 0x7e, 0xc4, 0xe0, 0x79, 0x39, 0xe5, 0xee, 0x54, 0xf1, 0xd6, 0x0f,
0x9e, 0xf8, 0xc1, 0x2f, 0xfc, 0xe0, 0xbd, 0x4d, 0x1f, 0xe9, 0x80, 0x45, 0xa7, 0x13, 0xdb, 0xa6,
0x83, 0xda, 0x05, 0xf0, 0x18, 0xc5, 0x6d, 0x14, 0xe7, 0xa7, 0xfc, 0x26, 0xbd, 0xd1, 0x5b, 0xa2,
0x80, 0xe0, 0xac, 0x09, 0x70, 0xf4, 0x7e, 0x9d, 0xb2, 0x04, 0x3f, 0x06, 0xdf, 0xe8, 0x12, 0xd8,
0x07, 0x42, 0xd7, 0xdb, 0x36, 0xec, 0x16, 0x4f, 0x3f, 0xc8, 0x9f, 0x47, 0x91, 0xad, 0x44, 0x65,
0xbe, 0xfb, 0xe6, 0xdb, 0x8f, 0x77, 0x6b, 0xdb, 0x6c, 0x2b, 0x7e, 0xb5, 0x66, 0x24, 0x3a, 0xaa,
0x3f, 0x13, 0x3a, 0x58, 0x4e, 0x80, 0x89, 0xbf, 0xf3, 0xec, 0xcc, 0x6a, 0x45, 0x7c, 0x0f, 0x23,
0xdf, 0xfd, 0xbc, 0x97, 0xef, 0xbd, 0xee, 0xcc, 0x3e, 0x12, 0x3a, 0x78, 0x00, 0x15, 0x20, 0xfc,
0x27, 0x2e, 0x1f, 0xf6, 0xb2, 0xbe, 0x3f, 0xfe, 0xb2, 0x18, 0x92, 0xaf, 0x8b, 0x21, 0xf9, 0xbe,
0x18, 0x92, 0x67, 0x77, 0xff, 0x61, 0xb9, 0xcb, 0x4a, 0x83, 0xc1, 0xb4, 0xca, 0x64, 0x10, 0xf7,
0xf9, 0xce, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x07, 0xb9, 0x98, 0x4b, 0x83, 0x04, 0x00, 0x00,
}

View file

@ -0,0 +1,228 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: server/certificate/certificate.proto
/*
Package certificate is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package certificate
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/status"
)
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var (
filter_CertificateService_List_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_CertificateService_List_0(ctx context.Context, marshaler runtime.Marshaler, client CertificateServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RepositoryCertificateQuery
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_CertificateService_List_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.List(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
var (
filter_CertificateService_Create_0 = &utilities.DoubleArray{Encoding: map[string]int{"certificates": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
)
func request_CertificateService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client CertificateServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RepositoryCertificateCreateRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Certificates); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_CertificateService_Create_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
var (
filter_CertificateService_Delete_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_CertificateService_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client CertificateServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RepositoryCertificateQuery
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_CertificateService_Delete_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterCertificateServiceHandlerFromEndpoint is same as RegisterCertificateServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterCertificateServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterCertificateServiceHandler(ctx, mux, conn)
}
// RegisterCertificateServiceHandler registers the http handlers for service CertificateService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterCertificateServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterCertificateServiceHandlerClient(ctx, mux, NewCertificateServiceClient(conn))
}
// RegisterCertificateServiceHandler registers the http handlers for service CertificateService to "mux".
// The handlers forward requests to the grpc endpoint over the given implementation of "CertificateServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "CertificateServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "CertificateServiceClient" to call the correct interceptors.
func RegisterCertificateServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client CertificateServiceClient) error {
mux.Handle("GET", pattern_CertificateService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CertificateService_List_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CertificateService_List_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_CertificateService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CertificateService_Create_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CertificateService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_CertificateService_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_CertificateService_Delete_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_CertificateService_Delete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_CertificateService_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "certificates"}, ""))
pattern_CertificateService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "certificates"}, ""))
pattern_CertificateService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "certificates"}, ""))
)
var (
forward_CertificateService_List_0 = runtime.ForwardResponseMessage
forward_CertificateService_Create_0 = runtime.ForwardResponseMessage
forward_CertificateService_Delete_0 = runtime.ForwardResponseMessage
)

View file

@ -47,7 +47,7 @@ func (m *RepoAppsQuery) Reset() { *m = RepoAppsQuery{} }
func (m *RepoAppsQuery) String() string { return proto.CompactTextString(m) }
func (*RepoAppsQuery) ProtoMessage() {}
func (*RepoAppsQuery) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{0}
return fileDescriptor_repository_fd679778f6b4cef4, []int{0}
}
func (m *RepoAppsQuery) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -103,7 +103,7 @@ func (m *AppInfo) Reset() { *m = AppInfo{} }
func (m *AppInfo) String() string { return proto.CompactTextString(m) }
func (*AppInfo) ProtoMessage() {}
func (*AppInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{1}
return fileDescriptor_repository_fd679778f6b4cef4, []int{1}
}
func (m *AppInfo) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -162,7 +162,7 @@ func (m *RepoAppDetailsQuery) Reset() { *m = RepoAppDetailsQuery{} }
func (m *RepoAppDetailsQuery) String() string { return proto.CompactTextString(m) }
func (*RepoAppDetailsQuery) ProtoMessage() {}
func (*RepoAppDetailsQuery) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{2}
return fileDescriptor_repository_fd679778f6b4cef4, []int{2}
}
func (m *RepoAppDetailsQuery) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -238,7 +238,7 @@ func (m *RepoAppsResponse) Reset() { *m = RepoAppsResponse{} }
func (m *RepoAppsResponse) String() string { return proto.CompactTextString(m) }
func (*RepoAppsResponse) ProtoMessage() {}
func (*RepoAppsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{3}
return fileDescriptor_repository_fd679778f6b4cef4, []int{3}
}
func (m *RepoAppsResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -286,7 +286,7 @@ func (m *RepoQuery) Reset() { *m = RepoQuery{} }
func (m *RepoQuery) String() string { return proto.CompactTextString(m) }
func (*RepoQuery) ProtoMessage() {}
func (*RepoQuery) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{4}
return fileDescriptor_repository_fd679778f6b4cef4, []int{4}
}
func (m *RepoQuery) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -322,6 +322,91 @@ func (m *RepoQuery) GetRepo() string {
return ""
}
// RepoAccessQuery is a query for checking access to a repo
type RepoAccessQuery struct {
// The URL to the repo
Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
// Username for accessing repo
Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"`
// Password for accessing repo
Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
// Private key data for accessing SSH repository
SshPrivateKey string `protobuf:"bytes,4,opt,name=sshPrivateKey,proto3" json:"sshPrivateKey,omitempty"`
// Whether to skip certificate or host key validation
Insecure bool `protobuf:"varint,5,opt,name=insecure,proto3" json:"insecure,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RepoAccessQuery) Reset() { *m = RepoAccessQuery{} }
func (m *RepoAccessQuery) String() string { return proto.CompactTextString(m) }
func (*RepoAccessQuery) ProtoMessage() {}
func (*RepoAccessQuery) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_fd679778f6b4cef4, []int{5}
}
func (m *RepoAccessQuery) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *RepoAccessQuery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_RepoAccessQuery.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalTo(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (dst *RepoAccessQuery) XXX_Merge(src proto.Message) {
xxx_messageInfo_RepoAccessQuery.Merge(dst, src)
}
func (m *RepoAccessQuery) XXX_Size() int {
return m.Size()
}
func (m *RepoAccessQuery) XXX_DiscardUnknown() {
xxx_messageInfo_RepoAccessQuery.DiscardUnknown(m)
}
var xxx_messageInfo_RepoAccessQuery proto.InternalMessageInfo
func (m *RepoAccessQuery) GetRepo() string {
if m != nil {
return m.Repo
}
return ""
}
func (m *RepoAccessQuery) GetUsername() string {
if m != nil {
return m.Username
}
return ""
}
func (m *RepoAccessQuery) GetPassword() string {
if m != nil {
return m.Password
}
return ""
}
func (m *RepoAccessQuery) GetSshPrivateKey() string {
if m != nil {
return m.SshPrivateKey
}
return ""
}
func (m *RepoAccessQuery) GetInsecure() bool {
if m != nil {
return m.Insecure
}
return false
}
type RepoResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@ -332,7 +417,7 @@ func (m *RepoResponse) Reset() { *m = RepoResponse{} }
func (m *RepoResponse) String() string { return proto.CompactTextString(m) }
func (*RepoResponse) ProtoMessage() {}
func (*RepoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{5}
return fileDescriptor_repository_fd679778f6b4cef4, []int{6}
}
func (m *RepoResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -373,7 +458,7 @@ func (m *RepoCreateRequest) Reset() { *m = RepoCreateRequest{} }
func (m *RepoCreateRequest) String() string { return proto.CompactTextString(m) }
func (*RepoCreateRequest) ProtoMessage() {}
func (*RepoCreateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{6}
return fileDescriptor_repository_fd679778f6b4cef4, []int{7}
}
func (m *RepoCreateRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -427,7 +512,7 @@ func (m *RepoUpdateRequest) Reset() { *m = RepoUpdateRequest{} }
func (m *RepoUpdateRequest) String() string { return proto.CompactTextString(m) }
func (*RepoUpdateRequest) ProtoMessage() {}
func (*RepoUpdateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_repository_085a58cd976bcc45, []int{7}
return fileDescriptor_repository_fd679778f6b4cef4, []int{8}
}
func (m *RepoUpdateRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@ -469,6 +554,7 @@ func init() {
proto.RegisterType((*RepoAppDetailsQuery)(nil), "repository.RepoAppDetailsQuery")
proto.RegisterType((*RepoAppsResponse)(nil), "repository.RepoAppsResponse")
proto.RegisterType((*RepoQuery)(nil), "repository.RepoQuery")
proto.RegisterType((*RepoAccessQuery)(nil), "repository.RepoAccessQuery")
proto.RegisterType((*RepoResponse)(nil), "repository.RepoResponse")
proto.RegisterType((*RepoCreateRequest)(nil), "repository.RepoCreateRequest")
proto.RegisterType((*RepoUpdateRequest)(nil), "repository.RepoUpdateRequest")
@ -497,6 +583,8 @@ type RepositoryServiceClient interface {
Update(ctx context.Context, in *RepoUpdateRequest, opts ...grpc.CallOption) (*v1alpha1.Repository, error)
// Delete deletes a repo
Delete(ctx context.Context, in *RepoQuery, opts ...grpc.CallOption) (*RepoResponse, error)
// ValidateAccess validates access to a repository with given parameters
ValidateAccess(ctx context.Context, in *RepoAccessQuery, opts ...grpc.CallOption) (*RepoResponse, error)
}
type repositoryServiceClient struct {
@ -561,6 +649,15 @@ func (c *repositoryServiceClient) Delete(ctx context.Context, in *RepoQuery, opt
return out, nil
}
func (c *repositoryServiceClient) ValidateAccess(ctx context.Context, in *RepoAccessQuery, opts ...grpc.CallOption) (*RepoResponse, error) {
out := new(RepoResponse)
err := c.cc.Invoke(ctx, "/repository.RepositoryService/ValidateAccess", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for RepositoryService service
type RepositoryServiceServer interface {
@ -576,6 +673,8 @@ type RepositoryServiceServer interface {
Update(context.Context, *RepoUpdateRequest) (*v1alpha1.Repository, error)
// Delete deletes a repo
Delete(context.Context, *RepoQuery) (*RepoResponse, error)
// ValidateAccess validates access to a repository with given parameters
ValidateAccess(context.Context, *RepoAccessQuery) (*RepoResponse, error)
}
func RegisterRepositoryServiceServer(s *grpc.Server, srv RepositoryServiceServer) {
@ -690,6 +789,24 @@ func _RepositoryService_Delete_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
func _RepositoryService_ValidateAccess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RepoAccessQuery)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RepositoryServiceServer).ValidateAccess(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/repository.RepositoryService/ValidateAccess",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RepositoryServiceServer).ValidateAccess(ctx, req.(*RepoAccessQuery))
}
return interceptor(ctx, in, info, handler)
}
var _RepositoryService_serviceDesc = grpc.ServiceDesc{
ServiceName: "repository.RepositoryService",
HandlerType: (*RepositoryServiceServer)(nil),
@ -718,6 +835,10 @@ var _RepositoryService_serviceDesc = grpc.ServiceDesc{
MethodName: "Delete",
Handler: _RepositoryService_Delete_Handler,
},
{
MethodName: "ValidateAccess",
Handler: _RepositoryService_ValidateAccess_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "server/repository/repository.proto",
@ -908,6 +1029,61 @@ func (m *RepoQuery) MarshalTo(dAtA []byte) (int, error) {
return i, nil
}
func (m *RepoAccessQuery) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RepoAccessQuery) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Repo) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintRepository(dAtA, i, uint64(len(m.Repo)))
i += copy(dAtA[i:], m.Repo)
}
if len(m.Username) > 0 {
dAtA[i] = 0x12
i++
i = encodeVarintRepository(dAtA, i, uint64(len(m.Username)))
i += copy(dAtA[i:], m.Username)
}
if len(m.Password) > 0 {
dAtA[i] = 0x1a
i++
i = encodeVarintRepository(dAtA, i, uint64(len(m.Password)))
i += copy(dAtA[i:], m.Password)
}
if len(m.SshPrivateKey) > 0 {
dAtA[i] = 0x22
i++
i = encodeVarintRepository(dAtA, i, uint64(len(m.SshPrivateKey)))
i += copy(dAtA[i:], m.SshPrivateKey)
}
if m.Insecure {
dAtA[i] = 0x28
i++
if m.Insecure {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i++
}
if m.XXX_unrecognized != nil {
i += copy(dAtA[i:], m.XXX_unrecognized)
}
return i, nil
}
func (m *RepoResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@ -1101,6 +1277,34 @@ func (m *RepoQuery) Size() (n int) {
return n
}
func (m *RepoAccessQuery) Size() (n int) {
var l int
_ = l
l = len(m.Repo)
if l > 0 {
n += 1 + l + sovRepository(uint64(l))
}
l = len(m.Username)
if l > 0 {
n += 1 + l + sovRepository(uint64(l))
}
l = len(m.Password)
if l > 0 {
n += 1 + l + sovRepository(uint64(l))
}
l = len(m.SshPrivateKey)
if l > 0 {
n += 1 + l + sovRepository(uint64(l))
}
if m.Insecure {
n += 2
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func (m *RepoResponse) Size() (n int) {
var l int
_ = l
@ -1736,6 +1940,193 @@ func (m *RepoQuery) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *RepoAccessQuery) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RepoAccessQuery: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RepoAccessQuery: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Repo", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRepository
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Repo = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRepository
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Username = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRepository
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Password = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SshPrivateKey", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthRepository
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SshPrivateKey = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 5:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Insecure", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRepository
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.Insecure = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skipRepository(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthRepository
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RepoResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@ -2081,52 +2472,58 @@ var (
)
func init() {
proto.RegisterFile("server/repository/repository.proto", fileDescriptor_repository_085a58cd976bcc45)
proto.RegisterFile("server/repository/repository.proto", fileDescriptor_repository_fd679778f6b4cef4)
}
var fileDescriptor_repository_085a58cd976bcc45 = []byte{
// 679 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xc1, 0x6e, 0x13, 0x3d,
0x10, 0xd6, 0xb6, 0x69, 0xda, 0xba, 0x7f, 0x7f, 0x81, 0x5b, 0xaa, 0xb0, 0xa4, 0x6d, 0x64, 0x0e,
0xa4, 0x48, 0xac, 0x95, 0x94, 0x03, 0x42, 0x20, 0xd4, 0x52, 0x04, 0x15, 0x5c, 0x58, 0xc4, 0x01,
0x0e, 0xa0, 0xed, 0x66, 0xd8, 0x98, 0x6c, 0xd6, 0xc6, 0x76, 0x22, 0x55, 0x55, 0x2f, 0x48, 0xf4,
0x01, 0xe0, 0xce, 0x9d, 0x37, 0xe1, 0xc0, 0x01, 0x89, 0x17, 0x40, 0x15, 0x0f, 0x82, 0xec, 0xdd,
0x6c, 0xb6, 0x69, 0xda, 0x22, 0x54, 0x71, 0x9b, 0x1d, 0xcf, 0x37, 0xdf, 0x67, 0xcf, 0xe7, 0x35,
0x22, 0x0a, 0x64, 0x1f, 0x24, 0x95, 0x20, 0xb8, 0x62, 0x9a, 0xcb, 0xdd, 0x42, 0xe8, 0x09, 0xc9,
0x35, 0xc7, 0x68, 0x98, 0x71, 0x17, 0x23, 0x1e, 0x71, 0x9b, 0xa6, 0x26, 0x4a, 0x2b, 0xdc, 0x6a,
0xc4, 0x79, 0x14, 0x03, 0x0d, 0x04, 0xa3, 0x41, 0x92, 0x70, 0x1d, 0x68, 0xc6, 0x13, 0x95, 0xad,
0x92, 0xce, 0x2d, 0xe5, 0x31, 0x6e, 0x57, 0x43, 0x2e, 0x81, 0xf6, 0x1b, 0x34, 0x82, 0x04, 0x64,
0xa0, 0xa1, 0x95, 0xd5, 0x6c, 0x47, 0x4c, 0xb7, 0x7b, 0x3b, 0x5e, 0xc8, 0xbb, 0x34, 0x90, 0x96,
0xe2, 0xad, 0x0d, 0x6e, 0x84, 0x2d, 0x2a, 0x3a, 0x91, 0x01, 0x2b, 0x1a, 0x08, 0x11, 0xb3, 0xd0,
0x36, 0xa7, 0xfd, 0x46, 0x10, 0x8b, 0x76, 0x70, 0xbc, 0xd5, 0xe6, 0x69, 0xad, 0xec, 0x56, 0xce,
0xdc, 0x32, 0xb9, 0x87, 0xe6, 0x7d, 0x10, 0x7c, 0x43, 0x08, 0xf5, 0xb4, 0x07, 0x72, 0x17, 0x63,
0x54, 0x32, 0x45, 0x15, 0xa7, 0xe6, 0xd4, 0x67, 0x7d, 0x1b, 0x63, 0x17, 0xcd, 0x48, 0xe8, 0x33,
0xc5, 0x78, 0x52, 0x99, 0xb0, 0xf9, 0xfc, 0x9b, 0x34, 0xd0, 0xf4, 0x86, 0x10, 0xdb, 0xc9, 0x1b,
0x6e, 0xa0, 0x7a, 0x57, 0xc0, 0x00, 0x6a, 0x62, 0x93, 0x13, 0x81, 0x6e, 0x67, 0x30, 0x1b, 0x93,
0x6f, 0x0e, 0x5a, 0xc8, 0x48, 0xb7, 0x40, 0x07, 0x2c, 0xfe, 0x3b, 0xea, 0xbc, 0xf7, 0xe4, 0xb0,
0x37, 0x5e, 0x47, 0xa5, 0x36, 0xc4, 0xdd, 0x4a, 0xa9, 0xe6, 0xd4, 0xe7, 0x9a, 0xab, 0x5e, 0x61,
0xc3, 0x8f, 0x20, 0xee, 0x8e, 0x50, 0xfa, 0xb6, 0x18, 0xdf, 0x41, 0xd3, 0x1d, 0xc5, 0x93, 0x04,
0x74, 0x65, 0xca, 0xe2, 0x48, 0x11, 0xf7, 0x38, 0x5d, 0x1a, 0x85, 0x0e, 0x20, 0xe4, 0x2e, 0xba,
0x30, 0x38, 0x42, 0x1f, 0x94, 0xe0, 0x89, 0x02, 0xbc, 0x86, 0xa6, 0x98, 0x86, 0xae, 0xaa, 0x38,
0xb5, 0xc9, 0xfa, 0x5c, 0x73, 0xa1, 0xd8, 0x2f, 0x3b, 0x2e, 0x3f, 0xad, 0x20, 0xab, 0x68, 0xd6,
0xc0, 0x4f, 0x3c, 0x02, 0xf2, 0x3f, 0xfa, 0xcf, 0x14, 0x0c, 0x7a, 0x93, 0x03, 0x07, 0x5d, 0x34,
0x89, 0xfb, 0x12, 0x02, 0x0d, 0x3e, 0xbc, 0xeb, 0x81, 0xd2, 0xf8, 0x45, 0x01, 0x39, 0xd7, 0x7c,
0xe0, 0x0d, 0xbd, 0xe1, 0x0d, 0xbc, 0x61, 0x83, 0xd7, 0x61, 0xcb, 0x13, 0x9d, 0xc8, 0x33, 0x36,
0xf3, 0x0a, 0x36, 0xf3, 0x06, 0x36, 0xf3, 0xfc, 0x5c, 0x6a, 0x36, 0x83, 0x25, 0x54, 0xee, 0x09,
0x05, 0x52, 0xdb, 0x09, 0xcc, 0xf8, 0xd9, 0x17, 0x49, 0x52, 0x1d, 0xcf, 0x45, 0xeb, 0x9f, 0xe8,
0x68, 0x7e, 0x29, 0xa7, 0x84, 0x69, 0xf2, 0x19, 0xc8, 0x3e, 0x0b, 0x01, 0x1f, 0x38, 0xa8, 0xf4,
0x84, 0x29, 0x8d, 0x2f, 0x15, 0x0f, 0x39, 0x3f, 0x52, 0x77, 0xfb, 0x5c, 0x24, 0x18, 0x06, 0x52,
0x7d, 0xff, 0xe3, 0xd7, 0xa7, 0x89, 0x25, 0xbc, 0x68, 0x6f, 0x78, 0xbf, 0x31, 0xbc, 0x4e, 0x0c,
0x14, 0xee, 0xa2, 0x19, 0x53, 0x65, 0x7c, 0x80, 0x2f, 0x8f, 0x6a, 0xc9, 0x2f, 0x98, 0x5b, 0x1d,
0xb7, 0x94, 0x0f, 0xb7, 0x6e, 0x29, 0x08, 0xae, 0x8d, 0xa3, 0xa0, 0x7b, 0xe6, 0x6b, 0xdf, 0xfc,
0x1d, 0x14, 0xfe, 0xe0, 0xa0, 0xf9, 0x87, 0x45, 0x5b, 0xe2, 0xd5, 0x31, 0x9d, 0x8b, 0x96, 0x75,
0xc9, 0xc9, 0x05, 0xb9, 0x00, 0x6a, 0x05, 0xac, 0xe1, 0x6b, 0x67, 0x09, 0xa0, 0x7b, 0xe6, 0xc2,
0xed, 0xe3, 0x8f, 0x0e, 0x2a, 0xa7, 0x56, 0xc4, 0xcb, 0xa3, 0xfd, 0x8f, 0x58, 0xd4, 0x3d, 0x1f,
0x33, 0x10, 0x62, 0x15, 0x56, 0xc9, 0xd8, 0x29, 0xdc, 0x4e, 0x2d, 0xfb, 0xd9, 0x41, 0xe5, 0xd4,
0x97, 0xc7, 0x45, 0x1d, 0xf1, 0xeb, 0x79, 0x89, 0xf2, 0xac, 0xa8, 0xba, 0x7b, 0xca, 0xdc, 0xac,
0x8e, 0xfd, 0x4c, 0xe0, 0x2b, 0x54, 0xde, 0x82, 0x18, 0x34, 0x9c, 0x64, 0xdb, 0xca, 0x68, 0x3a,
0x9f, 0xd0, 0x55, 0x4b, 0xb5, 0x7c, 0xfd, 0xca, 0x29, 0x13, 0xda, 0xdc, 0xfc, 0x7a, 0xb8, 0xe2,
0x7c, 0x3f, 0x5c, 0x71, 0x7e, 0x1e, 0xae, 0x38, 0x2f, 0x6f, 0xfe, 0xc1, 0xa3, 0x13, 0xc6, 0x0c,
0x12, 0x5d, 0x78, 0x21, 0x76, 0xca, 0xf6, 0x89, 0x58, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x24,
0xdc, 0xe6, 0x9b, 0x3b, 0x07, 0x00, 0x00,
var fileDescriptor_repository_fd679778f6b4cef4 = []byte{
// 784 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x4f, 0x6f, 0x1b, 0x45,
0x14, 0xd7, 0x26, 0x8e, 0xe3, 0x4c, 0x48, 0x80, 0x49, 0x88, 0xcc, 0xc6, 0x49, 0xac, 0x21, 0x12,
0x0e, 0x82, 0x5d, 0xd9, 0xe1, 0x80, 0x10, 0x08, 0x25, 0x04, 0x41, 0x14, 0x0e, 0xb0, 0x08, 0x24,
0x38, 0x80, 0x36, 0xeb, 0xc7, 0x7a, 0xf0, 0x7a, 0x67, 0x98, 0x19, 0x2f, 0xb2, 0xa2, 0x5c, 0x90,
0x9a, 0x0f, 0xd0, 0xde, 0xdb, 0x5b, 0x3f, 0x4b, 0x0f, 0x3d, 0x54, 0xea, 0x17, 0xa8, 0xa2, 0x7e,
0x90, 0x6a, 0x66, 0xff, 0x78, 0xe3, 0x38, 0x4e, 0x55, 0x45, 0xbd, 0xbd, 0x79, 0xf3, 0xde, 0xfb,
0xfd, 0xde, 0x9f, 0x79, 0x83, 0x88, 0x04, 0x91, 0x80, 0x70, 0x05, 0x70, 0x26, 0xa9, 0x62, 0x62,
0x54, 0x12, 0x1d, 0x2e, 0x98, 0x62, 0x18, 0x8d, 0x35, 0xf6, 0x7a, 0xc8, 0x42, 0x66, 0xd4, 0xae,
0x96, 0x52, 0x0b, 0xbb, 0x11, 0x32, 0x16, 0x46, 0xe0, 0xfa, 0x9c, 0xba, 0x7e, 0x1c, 0x33, 0xe5,
0x2b, 0xca, 0x62, 0x99, 0xdd, 0x92, 0xfe, 0x17, 0xd2, 0xa1, 0xcc, 0xdc, 0x06, 0x4c, 0x80, 0x9b,
0xb4, 0xdd, 0x10, 0x62, 0x10, 0xbe, 0x82, 0x6e, 0x66, 0x73, 0x1c, 0x52, 0xd5, 0x1b, 0x9e, 0x3a,
0x01, 0x1b, 0xb8, 0xbe, 0x30, 0x10, 0xff, 0x18, 0xe1, 0xb3, 0xa0, 0xeb, 0xf2, 0x7e, 0xa8, 0x9d,
0xa5, 0xeb, 0x73, 0x1e, 0xd1, 0xc0, 0x04, 0x77, 0x93, 0xb6, 0x1f, 0xf1, 0x9e, 0x7f, 0x3d, 0xd4,
0xe1, 0xac, 0x50, 0x26, 0x95, 0x5b, 0x53, 0x26, 0xdf, 0xa0, 0x15, 0x0f, 0x38, 0x3b, 0xe0, 0x5c,
0xfe, 0x3c, 0x04, 0x31, 0xc2, 0x18, 0x55, 0xb4, 0x51, 0xdd, 0x6a, 0x5a, 0xad, 0x25, 0xcf, 0xc8,
0xd8, 0x46, 0x35, 0x01, 0x09, 0x95, 0x94, 0xc5, 0xf5, 0x39, 0xa3, 0x2f, 0xce, 0xa4, 0x8d, 0x16,
0x0f, 0x38, 0x3f, 0x8e, 0xff, 0x66, 0xda, 0x55, 0x8d, 0x38, 0xe4, 0xae, 0x5a, 0xd6, 0x3a, 0xee,
0xab, 0x5e, 0xe6, 0x66, 0x64, 0xf2, 0xd4, 0x42, 0x6b, 0x19, 0xe8, 0x11, 0x28, 0x9f, 0x46, 0x6f,
0x06, 0x5d, 0xc4, 0x9e, 0x1f, 0xc7, 0xc6, 0xfb, 0xa8, 0xd2, 0x83, 0x68, 0x50, 0xaf, 0x34, 0xad,
0xd6, 0x72, 0x67, 0xc7, 0x29, 0x25, 0xfc, 0x03, 0x44, 0x83, 0x09, 0x48, 0xcf, 0x18, 0xe3, 0xaf,
0xd0, 0x62, 0x5f, 0xb2, 0x38, 0x06, 0x55, 0x5f, 0x30, 0x7e, 0xa4, 0xec, 0x77, 0x92, 0x5e, 0x4d,
0xba, 0xe6, 0x2e, 0xe4, 0x6b, 0xf4, 0x5e, 0x5e, 0x42, 0x0f, 0x24, 0x67, 0xb1, 0x04, 0xbc, 0x87,
0x16, 0xa8, 0x82, 0x81, 0xac, 0x5b, 0xcd, 0xf9, 0xd6, 0x72, 0x67, 0xad, 0x1c, 0x2f, 0x2b, 0x97,
0x97, 0x5a, 0x90, 0x1d, 0xb4, 0xa4, 0xdd, 0x6f, 0x2c, 0x01, 0x79, 0x64, 0xa1, 0x77, 0x0d, 0x40,
0x10, 0x80, 0x9c, 0x5d, 0xaa, 0xa1, 0x04, 0x11, 0xfb, 0x03, 0xc8, 0x4b, 0x95, 0x9f, 0xf5, 0x1d,
0xf7, 0xa5, 0xfc, 0x8f, 0x89, 0x6e, 0x56, 0xae, 0xe2, 0x8c, 0x77, 0xd1, 0x8a, 0x94, 0xbd, 0x9f,
0x04, 0x4d, 0x7c, 0x05, 0x27, 0x30, 0x32, 0xb5, 0x5b, 0xf2, 0xae, 0x2a, 0x75, 0x04, 0x1a, 0x4b,
0x08, 0x86, 0x02, 0x4c, 0x91, 0x6a, 0x5e, 0x71, 0x26, 0xab, 0xe8, 0x1d, 0x4d, 0x30, 0xcf, 0x9e,
0x5c, 0x58, 0xe8, 0x7d, 0xad, 0xf8, 0x56, 0x80, 0xaf, 0xc0, 0x83, 0x7f, 0x87, 0x20, 0x15, 0xfe,
0xbd, 0xc4, 0x79, 0xb9, 0xf3, 0x9d, 0x33, 0x9e, 0x5e, 0x27, 0x9f, 0x5e, 0x23, 0xfc, 0x15, 0x74,
0x1d, 0xde, 0x0f, 0x1d, 0xfd, 0x10, 0x9c, 0xd2, 0x43, 0x70, 0xf2, 0x87, 0xe0, 0x78, 0x45, 0x31,
0xb3, 0xd4, 0x37, 0x50, 0x75, 0xc8, 0x25, 0x08, 0x65, 0x12, 0xaf, 0x79, 0xd9, 0x89, 0xc4, 0x29,
0x8f, 0x5f, 0x79, 0xf7, 0xad, 0xf0, 0xe8, 0x3c, 0x5e, 0x4c, 0x01, 0x53, 0xe5, 0x2f, 0x20, 0x12,
0x1a, 0x00, 0xbe, 0xb0, 0x50, 0xe5, 0x47, 0x2a, 0x15, 0xfe, 0xa0, 0x3c, 0x06, 0x45, 0xd3, 0xed,
0xe3, 0x3b, 0xa1, 0xa0, 0x11, 0x48, 0xe3, 0xff, 0xe7, 0x2f, 0x1f, 0xcc, 0x6d, 0xe0, 0x75, 0xb3,
0x83, 0x92, 0xf6, 0xf8, 0xc1, 0x53, 0x90, 0x78, 0x80, 0x6a, 0xda, 0x4a, 0x4f, 0x2a, 0xfe, 0x70,
0x92, 0x4b, 0xb1, 0x02, 0xec, 0xc6, 0xb4, 0xab, 0xa2, 0xb9, 0x2d, 0x03, 0x41, 0x70, 0x73, 0x1a,
0x84, 0x7b, 0xa6, 0x4f, 0xe7, 0x7a, 0x7f, 0x49, 0x7c, 0xcf, 0x42, 0x2b, 0xdf, 0x97, 0x1f, 0x0e,
0xde, 0x99, 0x12, 0xb9, 0xfc, 0xa8, 0x6c, 0x72, 0xb3, 0x41, 0x41, 0xc0, 0x35, 0x04, 0xf6, 0xf0,
0xc7, 0xb7, 0x11, 0x70, 0xcf, 0xf4, 0x4a, 0x38, 0xc7, 0xf7, 0x2d, 0x54, 0x4d, 0x47, 0x11, 0x6f,
0x4d, 0xc6, 0xbf, 0x32, 0xa2, 0xf6, 0xdd, 0x0c, 0x03, 0x21, 0x86, 0x61, 0x83, 0x4c, 0xed, 0xc2,
0x97, 0xe9, 0xc8, 0x3e, 0xb4, 0x50, 0x35, 0x9d, 0xcb, 0xeb, 0xa4, 0xae, 0xcc, 0xeb, 0x5d, 0x91,
0x72, 0x0c, 0xa9, 0x96, 0x3d, 0xa3, 0x6f, 0x86, 0xc7, 0x79, 0x46, 0xf0, 0x4f, 0x54, 0x3d, 0x82,
0x08, 0x14, 0xdc, 0x34, 0xb6, 0xf5, 0x49, 0x75, 0xd1, 0xa1, 0x8f, 0x0c, 0xd4, 0xd6, 0x27, 0x9b,
0x33, 0x3a, 0x84, 0xcf, 0xd0, 0xea, 0x6f, 0x7e, 0x44, 0x75, 0xa6, 0xe9, 0x66, 0xc3, 0x9b, 0xd7,
0x9a, 0x3f, 0xde, 0x78, 0x33, 0xd0, 0x3a, 0x06, 0xed, 0x53, 0xb2, 0x3b, 0x6b, 0x1e, 0x92, 0x0c,
0x2a, 0x4d, 0xee, 0xf0, 0xf0, 0xc9, 0xe5, 0xb6, 0xf5, 0xec, 0x72, 0xdb, 0x7a, 0x71, 0xb9, 0x6d,
0xfd, 0xf1, 0xf9, 0x6b, 0xfc, 0xc9, 0x41, 0x44, 0x21, 0x56, 0xa5, 0x0f, 0xf4, 0xb4, 0x6a, 0x7e,
0xd0, 0xfd, 0x57, 0x01, 0x00, 0x00, 0xff, 0xff, 0x44, 0x05, 0xf6, 0xec, 0x5a, 0x08, 0x00, 0x00,
}

View file

@ -205,6 +205,45 @@ func request_RepositoryService_Delete_0(ctx context.Context, marshaler runtime.M
}
var (
filter_RepositoryService_ValidateAccess_0 = &utilities.DoubleArray{Encoding: map[string]int{"repo": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}}
)
func request_RepositoryService_ValidateAccess_0(ctx context.Context, marshaler runtime.Marshaler, client RepositoryServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RepoAccessQuery
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Repo); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["repo"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "repo")
}
protoReq.Repo, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "repo", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_RepositoryService_ValidateAccess_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ValidateAccess(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterRepositoryServiceHandlerFromEndpoint is same as RegisterRepositoryServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterRepositoryServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@ -417,6 +456,35 @@ func RegisterRepositoryServiceHandlerClient(ctx context.Context, mux *runtime.Se
})
mux.Handle("POST", pattern_RepositoryService_ValidateAccess_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_RepositoryService_ValidateAccess_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_RepositoryService_ValidateAccess_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -432,6 +500,8 @@ var (
pattern_RepositoryService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "repositories", "repo.repo"}, ""))
pattern_RepositoryService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "repositories", "repo"}, ""))
pattern_RepositoryService_ValidateAccess_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "repositories", "repo", "validate"}, ""))
)
var (
@ -446,4 +516,6 @@ var (
forward_RepositoryService_Update_0 = runtime.ForwardResponseMessage
forward_RepositoryService_Delete_0 = runtime.ForwardResponseMessage
forward_RepositoryService_ValidateAccess_0 = runtime.ForwardResponseMessage
)

View file

@ -2,6 +2,11 @@ API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,ConnectionState,ModifiedAt
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,JWTToken,ExpiresAt
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,JWTToken,IssuedAt
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepositoryCertificate,CertData
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepositoryCertificate,CertFingerprint
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepositoryCertificate,CertSubType
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepositoryCertificate,CertType
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,RepositoryCertificate,ServerName
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,ResourceActionDefinition,ActionLua
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,ResourceActions,ActionDiscoveryLua
API rule violation: names_match,github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1,ResourceOverride,HealthLua

File diff suppressed because it is too large Load diff

View file

@ -484,17 +484,51 @@ message ProjectRole {
// Repository is a Git repository holding application configurations
message Repository {
// URL of the repo
optional string repo = 1;
// Username for authenticating at the repo server
optional string username = 2;
// Password for authenticating at the repo server
optional string password = 3;
// SSH private key data for authenticating at the repo server
optional string sshPrivateKey = 4;
optional ConnectionState connectionState = 5;
// InsecureIgnoreHostKey should not be used anymore, Insecure is favoured
optional bool insecureIgnoreHostKey = 6;
// Whether the repo is insecure
optional bool insecure = 7;
}
// A RepositoryCertificate is either SSH known hosts entry or TLS certificate
message RepositoryCertificate {
// Name of the server the certificate is intended for
optional string servername = 1;
// Type of certificate - currently "https" or "ssh"
optional string type = 2;
// The sub type of the cert, i.e. "ssh-rsa"
optional string cipher = 3;
// Actual certificate data, protocol dependent
optional bytes certdata = 4;
// Certificate fingerprint
optional string certfingerprint = 5;
}
// RepositoryCertificateList is a collection of RepositoryCertificates
message RepositoryCertificateList {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1;
// List of certificates to be processed
repeated RepositoryCertificate items = 2;
}
// RepositoryList is a collection of Repositories.

View file

@ -55,6 +55,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.OperationState": schema_pkg_apis_application_v1alpha1_OperationState(ref),
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ProjectRole": schema_pkg_apis_application_v1alpha1_ProjectRole(ref),
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.Repository": schema_pkg_apis_application_v1alpha1_Repository(ref),
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.RepositoryCertificate": schema_pkg_apis_application_v1alpha1_RepositoryCertificate(ref),
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.RepositoryCertificateList": schema_pkg_apis_application_v1alpha1_RepositoryCertificateList(ref),
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.RepositoryList": schema_pkg_apis_application_v1alpha1_RepositoryList(ref),
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ResourceAction": schema_pkg_apis_application_v1alpha1_ResourceAction(ref),
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.ResourceActionDefinition": schema_pkg_apis_application_v1alpha1_ResourceActionDefinition(ref),
@ -1749,26 +1751,30 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac
Properties: map[string]spec.Schema{
"repo": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
Description: "URL of the repo",
Type: []string{"string"},
Format: "",
},
},
"username": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
Description: "Username for authenticating at the repo server",
Type: []string{"string"},
Format: "",
},
},
"password": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
Description: "Password for authenticating at the repo server",
Type: []string{"string"},
Format: "",
},
},
"sshPrivateKey": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
Description: "SSH private key data for authenticating at the repo server",
Type: []string{"string"},
Format: "",
},
},
"connectionState": {
@ -1778,8 +1784,16 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac
},
"insecureIgnoreHostKey": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
Description: "InsecureIgnoreHostKey should not be used anymore, Insecure is favoured",
Type: []string{"boolean"},
Format: "",
},
},
"insecure": {
SchemaProps: spec.SchemaProps{
Description: "Whether the repo is insecure",
Type: []string{"boolean"},
Format: "",
},
},
},
@ -1791,6 +1805,89 @@ func schema_pkg_apis_application_v1alpha1_Repository(ref common.ReferenceCallbac
}
}
func schema_pkg_apis_application_v1alpha1_RepositoryCertificate(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "A RepositoryCertificate is either SSH known hosts entry or TLS certificate",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"servername": {
SchemaProps: spec.SchemaProps{
Description: "Name of the server the certificate is intended for",
Type: []string{"string"},
Format: "",
},
},
"type": {
SchemaProps: spec.SchemaProps{
Description: "Type of certificate - currently \"https\" or \"ssh\"",
Type: []string{"string"},
Format: "",
},
},
"cipher": {
SchemaProps: spec.SchemaProps{
Description: "The sub type of the cert, i.e. \"ssh-rsa\"",
Type: []string{"string"},
Format: "",
},
},
"certdata": {
SchemaProps: spec.SchemaProps{
Description: "Actual certificate data, protocol dependent",
Type: []string{"string"},
Format: "byte",
},
},
"certfingerprint": {
SchemaProps: spec.SchemaProps{
Description: "Certificate fingerprint",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"servername", "type", "cipher", "certdata", "certfingerprint"},
},
},
}
}
func schema_pkg_apis_application_v1alpha1_RepositoryCertificateList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "RepositoryCertificateList is a collection of RepositoryCertificates",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Description: "List of certificates to be processed",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.RepositoryCertificate"),
},
},
},
},
},
},
Required: []string{"items"},
},
},
Dependencies: []string{
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1.RepositoryCertificate", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_application_v1alpha1_RepositoryList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{

View file

@ -881,12 +881,19 @@ type ResourceActionParam struct {
// Repository is a Git repository holding application configurations
type Repository struct {
Repo string `json:"repo" protobuf:"bytes,1,opt,name=repo"`
Username string `json:"username,omitempty" protobuf:"bytes,2,opt,name=username"`
Password string `json:"password,omitempty" protobuf:"bytes,3,opt,name=password"`
SSHPrivateKey string `json:"sshPrivateKey,omitempty" protobuf:"bytes,4,opt,name=sshPrivateKey"`
ConnectionState ConnectionState `json:"connectionState,omitempty" protobuf:"bytes,5,opt,name=connectionState"`
InsecureIgnoreHostKey bool `json:"insecureIgnoreHostKey,omitempty" protobuf:"bytes,6,opt,name=insecureIgnoreHostKey"`
// URL of the repo
Repo string `json:"repo" protobuf:"bytes,1,opt,name=repo"`
// Username for authenticating at the repo server
Username string `json:"username,omitempty" protobuf:"bytes,2,opt,name=username"`
// Password for authenticating at the repo server
Password string `json:"password,omitempty" protobuf:"bytes,3,opt,name=password"`
// SSH private key data for authenticating at the repo server
SSHPrivateKey string `json:"sshPrivateKey,omitempty" protobuf:"bytes,4,opt,name=sshPrivateKey"`
ConnectionState ConnectionState `json:"connectionState,omitempty" protobuf:"bytes,5,opt,name=connectionState"`
// InsecureIgnoreHostKey should not be used anymore, Insecure is favoured
InsecureIgnoreHostKey bool `json:"insecureIgnoreHostKey,omitempty" protobuf:"bytes,6,opt,name=insecureIgnoreHostKey"`
// Whether the repo is insecure
Insecure bool `json:"insecure,omitempty" protobuf:"bytes,7,opt,name=insecure"`
}
func (m *Repository) HasCredentials() bool {
@ -899,6 +906,7 @@ func (m *Repository) CopyCredentialsFrom(source *Repository) {
m.Password = source.Password
m.SSHPrivateKey = source.SSHPrivateKey
m.InsecureIgnoreHostKey = source.InsecureIgnoreHostKey
m.Insecure = source.Insecure
}
}
@ -908,6 +916,27 @@ type RepositoryList struct {
Items []Repository `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// A RepositoryCertificate is either SSH known hosts entry or TLS certificate
type RepositoryCertificate struct {
// Name of the server the certificate is intended for
ServerName string `json:"servername" protobuf:"bytes,1,opt,name=servername"`
// Type of certificate - currently "https" or "ssh"
CertType string `json:"type" protobuf:"bytes,2,opt,name=type"`
// The sub type of the cert, i.e. "ssh-rsa"
CertSubType string `json:"cipher" protobuf:"bytes,3,opt,name=cipher"`
// Actual certificate data, protocol dependent
CertData []byte `json:"certdata" protobuf:"bytes,4,opt,name=certdata"`
// Certificate fingerprint
CertFingerprint string `json:"certfingerprint" protobuf:"bytes,5,opt,name=certfingerprint"`
}
// RepositoryCertificateList is a collection of RepositoryCertificates
type RepositoryCertificateList struct {
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// List of certificates to be processed
Items []RepositoryCertificate `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// AppProjectList is list of AppProject resources
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type AppProjectList struct {

View file

@ -1033,6 +1033,51 @@ func (in *Repository) DeepCopy() *Repository {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RepositoryCertificate) DeepCopyInto(out *RepositoryCertificate) {
*out = *in
if in.CertData != nil {
in, out := &in.CertData, &out.CertData
*out = make([]byte, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RepositoryCertificate.
func (in *RepositoryCertificate) DeepCopy() *RepositoryCertificate {
if in == nil {
return nil
}
out := new(RepositoryCertificate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RepositoryCertificateList) DeepCopyInto(out *RepositoryCertificateList) {
*out = *in
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]RepositoryCertificate, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RepositoryCertificateList.
func (in *RepositoryCertificateList) DeepCopy() *RepositoryCertificateList {
if in == nil {
return nil
}
out := new(RepositoryCertificateList)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RepositoryList) DeepCopyInto(out *RepositoryList) {
*out = *in

View file

@ -524,7 +524,7 @@ func (s *Service) newClientResolveRevision(repo *v1alpha1.Repository, revision s
func (s *Service) newClient(repo *v1alpha1.Repository) (git.Client, error) {
appPath := tempRepoPath(git.NormalizeGitURL(repo.Repo))
return s.gitFactory.NewClient(repo.Repo, appPath, repo.Username, repo.Password, repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
return s.gitFactory.NewClient(repo.Repo, appPath, repo.Username, repo.Password, repo.SSHPrivateKey, (repo.InsecureIgnoreHostKey || repo.Insecure))
}
func runCommand(command v1alpha1.Command, path string, env []string) (string, error) {
@ -753,7 +753,7 @@ func newCreds(repo *v1alpha1.Repository) git.Creds {
return git.NewHTTPSCreds(repo.Username, repo.Password)
}
if repo.SSHPrivateKey != "" {
return git.NewSSHCreds(repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
return git.NewSSHCreds(repo.SSHPrivateKey, (repo.InsecureIgnoreHostKey || repo.Insecure))
}
return git.NopCreds{}
}

View file

@ -64,7 +64,7 @@ func (f *fakeGitClientFactory) NewClient(repoURL, path, username, password, sshP
func TestGenerateYamlManifestInDir(t *testing.T) {
// update this value if we add/remove manifests
const countOfManifests = 23
const countOfManifests = 25
q := ManifestRequest{
ApplicationSource: &argoappv1.ApplicationSource{},

View file

@ -31,6 +31,9 @@ func newTestAccountServer(ctx context.Context) (*fake.Clientset, *Server, *sessi
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-cm",
Namespace: testNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
}, &v1.Secret{
ObjectMeta: metav1.ObjectMeta{

View file

@ -90,7 +90,13 @@ func fakeListDirResponse() *repository.FileList {
// return an ApplicationServiceServer which returns fake data
func newTestAppServer(objects ...runtime.Object) *Server {
kubeclientset := fake.NewSimpleClientset(&v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, Name: "argocd-cm"},
ObjectMeta: metav1.ObjectMeta{
Namespace: testNamespace,
Name: "argocd-cm",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
}, &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-secret",

View file

@ -25,7 +25,13 @@ var (
},
}
argoCDCm = corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{Name: "argocd-cm", Namespace: "default"},
ObjectMeta: v1.ObjectMeta{
Name: "argocd-cm",
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"statusbadge.enabled": "true",
},

View file

@ -0,0 +1,85 @@
package certificate
import (
"golang.org/x/net/context"
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/reposerver"
"github.com/argoproj/argo-cd/server/rbacpolicy"
"github.com/argoproj/argo-cd/util/cache"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/rbac"
)
// Server provides a Certificate service
type Server struct {
db db.ArgoDB
repoClientset reposerver.Clientset
enf *rbac.Enforcer
cache *cache.Cache
}
// NewServer returns a new instance of the Certificate service
func NewServer(
repoClientset reposerver.Clientset,
db db.ArgoDB,
enf *rbac.Enforcer,
cache *cache.Cache,
) *Server {
return &Server{
db: db,
repoClientset: repoClientset,
enf: enf,
cache: cache,
}
}
// TODO: RBAC policies are currently an all-or-nothing approach, so there is no
// fine grained control for certificate manipulation. Either a user has access
// to a given certificate operation (get/create/delete), or it doesn't.
// Returns a list of configured certificates that match the query
func (s *Server) List(ctx context.Context, q *certificatepkg.RepositoryCertificateQuery) (*appsv1.RepositoryCertificateList, error) {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceCertificates, rbacpolicy.ActionGet, ""); err != nil {
return nil, err
}
certList, err := s.db.ListRepoCertificates(ctx, &db.CertificateListSelector{
HostNamePattern: q.GetHostNamePattern(),
CertType: q.GetCertType(),
CertSubType: q.GetCertSubType(),
})
if err != nil {
return nil, err
}
return certList, nil
}
// Batch creates certificates for verifying repositories
func (s *Server) Create(ctx context.Context, q *certificatepkg.RepositoryCertificateCreateRequest) (*appsv1.RepositoryCertificateList, error) {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceCertificates, rbacpolicy.ActionCreate, ""); err != nil {
return nil, err
}
certs, err := s.db.CreateRepoCertificate(ctx, q.Certificates, q.Upsert)
if err != nil {
return nil, err
}
return certs, nil
}
// Batch deletes a list of certificates that match the query
func (s *Server) Delete(ctx context.Context, q *certificatepkg.RepositoryCertificateQuery) (*appsv1.RepositoryCertificateList, error) {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceCertificates, rbacpolicy.ActionDelete, ""); err != nil {
return nil, err
}
certs, err := s.db.RemoveRepoCertificates(ctx, &db.CertificateListSelector{
HostNamePattern: q.GetHostNamePattern(),
CertType: q.GetCertType(),
CertSubType: q.GetCertSubType(),
})
if err != nil {
return nil, err
}
return certs, nil
}

View file

@ -0,0 +1,51 @@
syntax = "proto3";
option go_package = "github.com/argoproj/argo-cd/pkg/apiclient/certificate";
// Certificate Service
//
// Certificate Service API performs CRUD actions against repository certificate
// resources.
package certificate;
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1/generated.proto";
// Message to query the server for configured repository certificates
message RepositoryCertificateQuery {
// A file-glob pattern (not regular expression) the host name has to match
string hostNamePattern = 1;
// The type of the certificate to match (ssh or https)
string certType = 2;
// The sub type of the certificate to match (protocol dependent, usually only used for ssh certs)
string certSubType = 3;
}
// Request to create a set of certificates
message RepositoryCertificateCreateRequest {
// List of certificates to be created
github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.RepositoryCertificateList certificates = 1;
// Whether to upsert already existing certificates
bool upsert = 2;
}
message RepositoryCertificateResponse {}
service CertificateService {
// List all available certificates
rpc List(RepositoryCertificateQuery) returns (github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.RepositoryCertificateList) {
option (google.api.http).get = "/api/v1/certificates";
}
// Creates the requested certificates on the server
rpc Create(RepositoryCertificateCreateRequest) returns (github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.RepositoryCertificateList) {
option (google.api.http) = {
post: "/api/v1/certificates"
body: "certificates"
};
}
rpc Delete(RepositoryCertificateQuery) returns (github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.RepositoryCertificateList) {
option (google.api.http).delete = "/api/v1/certificates";
}
}

View file

@ -31,7 +31,13 @@ const testNamespace = "default"
func TestProjectServer(t *testing.T) {
kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{
ObjectMeta: v1.ObjectMeta{Namespace: testNamespace, Name: "argocd-cm"},
ObjectMeta: v1.ObjectMeta{
Namespace: testNamespace,
Name: "argocd-cm",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
}, &corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: "argocd-secret",

View file

@ -18,6 +18,7 @@ const (
ResourceProjects = "projects"
ResourceApplications = "applications"
ResourceRepositories = "repositories"
ResourceCertificates = "certificates"
ActionGet = "get"
ActionCreate = "create"

View file

@ -60,7 +60,7 @@ func (s *Server) getConnectionState(ctx context.Context, url string) appsv1.Conn
}
repo, err := s.db.GetRepository(ctx, url)
if err == nil {
err = git.TestRepo(repo.Repo, repo.Username, repo.Password, repo.SSHPrivateKey, repo.InsecureIgnoreHostKey)
err = git.TestRepo(repo.Repo, repo.Username, repo.Password, repo.SSHPrivateKey, repo.Insecure)
}
if err != nil {
connectionState.Status = appsv1.ConnectionStatusFailed
@ -82,7 +82,11 @@ func (s *Server) List(ctx context.Context, q *repositorypkg.RepoQuery) (*appsv1.
items := make([]appsv1.Repository, 0)
for _, url := range urls {
if s.enf.Enforce(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, url) {
items = append(items, appsv1.Repository{Repo: url})
repo, err := s.db.GetRepository(ctx, url)
if err != nil {
return nil, err
}
items = append(items, appsv1.Repository{Repo: url, Insecure: (repo.InsecureIgnoreHostKey || repo.Insecure), Username: repo.Username})
}
}
err = util.RunAllAsync(len(items), func(i int) error {
@ -224,7 +228,7 @@ func (s *Server) Create(ctx context.Context, q *repositorypkg.RepoCreateRequest)
return nil, err
}
r := q.Repo
err := git.TestRepo(r.Repo, r.Username, r.Password, r.SSHPrivateKey, r.InsecureIgnoreHostKey)
err := git.TestRepo(r.Repo, r.Username, r.Password, r.SSHPrivateKey, (r.InsecureIgnoreHostKey || r.Insecure))
if err != nil {
return nil, err
}
@ -274,3 +278,18 @@ func (s *Server) Delete(ctx context.Context, q *repositorypkg.RepoQuery) (*repos
err := s.db.DeleteRepository(ctx, q.Repo)
return &repositorypkg.RepoResponse{}, err
}
// ValidateAccess checks whether access to a repository is possible with the
// given URL and credentials.
func (s *Server) ValidateAccess(ctx context.Context, q *repositorypkg.RepoAccessQuery) (*repositorypkg.RepoResponse, error) {
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionCreate, q.Repo); err != nil {
return nil, err
}
err := git.TestRepo(q.Repo, q.Username, q.Password, q.SshPrivateKey, q.Insecure)
if err != nil {
return nil, err
}
return &repositorypkg.RepoResponse{}, err
}

View file

@ -44,6 +44,20 @@ message RepoQuery {
string repo = 1;
}
// RepoAccessQuery is a query for checking access to a repo
message RepoAccessQuery {
// The URL to the repo
string repo = 1;
// Username for accessing repo
string username = 2;
// Password for accessing repo
string password = 3;
// Private key data for accessing SSH repository
string sshPrivateKey = 4;
// Whether to skip certificate or host key validation
bool insecure = 5;
}
message RepoResponse {}
message RepoCreateRequest {
@ -94,4 +108,12 @@ service RepositoryService {
option (google.api.http).delete = "/api/v1/repositories/{repo}";
}
// ValidateAccess validates access to a repository with given parameters
rpc ValidateAccess(RepoAccessQuery) returns (RepoResponse) {
option (google.api.http) = {
post: "/api/v1/repositories/{repo}/validate"
body: "repo"
};
}
}

View file

@ -47,6 +47,7 @@ import (
"github.com/argoproj/argo-cd/pkg/apiclient"
accountpkg "github.com/argoproj/argo-cd/pkg/apiclient/account"
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
@ -60,6 +61,7 @@ import (
"github.com/argoproj/argo-cd/server/account"
"github.com/argoproj/argo-cd/server/application"
"github.com/argoproj/argo-cd/server/badge"
"github.com/argoproj/argo-cd/server/certificate"
"github.com/argoproj/argo-cd/server/cluster"
"github.com/argoproj/argo-cd/server/project"
"github.com/argoproj/argo-cd/server/rbacpolicy"
@ -454,6 +456,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
projectService := project.NewServer(a.Namespace, a.KubeClientset, a.AppClientset, a.enf, projectLock, a.sessionMgr)
settingsService := settings.NewServer(a.settingsMgr)
accountService := account.NewServer(a.sessionMgr, a.settingsMgr)
certificateService := certificate.NewServer(a.RepoClientset, db, a.enf, a.Cache)
versionpkg.RegisterVersionServiceServer(grpcS, &version.Server{})
clusterpkg.RegisterClusterServiceServer(grpcS, clusterService)
applicationpkg.RegisterApplicationServiceServer(grpcS, applicationService)
@ -462,6 +465,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
settingspkg.RegisterSettingsServiceServer(grpcS, settingsService)
projectpkg.RegisterProjectServiceServer(grpcS, projectService)
accountpkg.RegisterAccountServiceServer(grpcS, accountService)
certificatepkg.RegisterCertificateServiceServer(grpcS, certificateService)
// Register reflection service on gRPC server.
reflection.Register(grpcS)
grpc_prometheus.Register(grpcS)
@ -538,6 +542,7 @@ func (a *ArgoCDServer) newHTTPServer(ctx context.Context, port int, grpcWebHandl
mustRegisterGWHandler(sessionpkg.RegisterSessionServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
mustRegisterGWHandler(settingspkg.RegisterSettingsServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
mustRegisterGWHandler(projectpkg.RegisterProjectServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
mustRegisterGWHandler(certificatepkg.RegisterCertificateServiceHandlerFromEndpoint, ctx, gwmux, endpoint, dOpts)
// Swagger UI
swagger.ServeSwaggerUI(mux, assets.SwaggerJSON, "/swagger-ui")

View file

@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIUGrTmW3qc39zqnE08e3qNDhUkeWswDQYJKoZIhvcNAQEL
BQAwbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMB4XDTE5MDcwODEzNTUwNVoXDTIwMDcwNzEzNTUw
NVowbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA3csSO13w7qQXKeSLNcpeuAe6wAjXYbRkRl6ariqzTEDcFTKmy2QiXJTKoEGn
bvwxq0T91var7rxY88SGL/qi8Zmo0tVSR0XvKSKcghFIkQOTyDmVgMPZGCvixt4q
gQ7hUVSk4KkFmtcqBVuvnzI1d/DKfZAGKdmGcfRpuAsnVhac3swP0w4Tl1BFrK9U
vuIkz4KwXG77s5oB8rMUnyuLasLsGNpvpvXhkcQRhp6vpcCO2bS7kOTTelAPIucw
P37qkOEdZdiWCLrr57dmhg6tmcVlmBMg6JtmfLxn2HQd9ZrCKlkWxMk5NYs6CAW5
kgbDZUWQTAsnHeoJKbcgtPkIbxDRxNpPukFMtbA4VEWv1EkODXy9FyEKDOI/PV6K
/80oLkgCIhCkP2mvwSFheU0RHTuZ0o0vVolP5TEOq5iufnDN4wrxqb12o//XLRc0
RiLqGVVxhFdyKCjVxcLfII9AAp5Tse4PMh6bf6jDfB3OMvGkhMbJWhKXdR2NUTl0
esKawMPRXIn5g3oBdNm8kyRsTTnvB567pU8uNSmA8j3jxfGCPynI8JdiwKQuW/+P
WgLIflgxqAfG85dVVOsFmF9o5o24dDslvv9yHnHH102c6ijPCg1EobqlyFzqqxOD
Wf2OPjIkzoTH+O27VRugnY/maIU1nshNO7ViRX5zIxEUtNMCAwEAAaNTMFEwHQYD
VR0OBBYEFNY4gDLgPBidogkmpO8nq5yAq5g+MB8GA1UdIwQYMBaAFNY4gDLgPBid
ogkmpO8nq5yAq5g+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AJ0WGioNtGNg3m6ywpmxNThorQD5ZvDMlmZlDVk78E2wfNyMhwbVhKhlAnONv0wv
kmsGjibY75nRZ+EK9PxSJ644841fryQXQ+bli5fhr7DW3uTKwaRsnzETJXRJuljq
6+c6Zyg1/mqwnyx7YvPgVh3w496DYx/jm6Fm1IEq3BzOmn6H/gGPq3gbURzEqI3h
P+kC2vJa8RZWrpa05Xk/Q1QUkErDX9vJghb9z3+GgirISZQzqWRghII/znv3NOE6
zoIgaaWNFn8KPeBVpUoboH+IhpgibsnbTbI0G7AMtFq6qm3kn/4DZ2N2tuh1G2tT
zR2Fh7hJbU7CrqxANrgnIoHG/nLSvzE24ckLb0Vj69uGQlwnZkn9fz6F7KytU+Az
NoB2rjufaB0GQi1azdboMvdGSOxhSCAR8otWT5yDrywCqVnEvjw0oxKmuRduNe2/
6AcG6TtK2/K+LHuhymiAwZM2qE6VD2odvb+tCzDkZOIeoIz/JcVlNpXE9FuVl250
9NWvugeghq7tUv81iJ8ninBefJ4lUfxAehTPQqX+zXcfxgjvMRCi/ig73nLyhmjx
r2AaraPFgrprnxUibP4L7jxdr+iiw5bWN9/B81PodrS7n5TNtnfnpZD6X6rThqOP
xO7Tr5lAo74vNUkF2EHNaI28/RGnJPm2TIxZqy4rNH6L
-----END CERTIFICATE-----

View file

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
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
XWyb96wrUlv+E8I=
-----END CERTIFICATE-----

View file

@ -0,0 +1,8 @@
# This file was automatically generated. DO NOT EDIT
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H

View file

@ -198,6 +198,9 @@ func NewFakeConfigMap() *apiv1.ConfigMap {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: FakeArgoCDNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: make(map[string]string),
}

273
util/cert/cert.go Normal file
View file

@ -0,0 +1,273 @@
// Utility functions for managing HTTPS server certificates and SSH known host
// entries for ArgoCD
package cert
import (
"bufio"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"golang.org/x/crypto/ssh"
)
// A struct representing an entry in the list of SSH known hosts.
type SSHKnownHostsEntry struct {
// Hostname the key is for
Host string
// The type of the key
SubType string
// The data of the key, including the type
Data string
// The SHA256 fingerprint of the key
Fingerprint string
}
// A representation of a TLS certificate
type TLSCertificate struct {
// Subject of the certificate
Subject string
// Issuer of the certificate
Issuer string
// Certificate data
Data string
}
// Helper struct for certificate selection
type CertificateListSelector struct {
// Pattern to match the hostname with
HostNamePattern string
// Type of certificate to match
CertType string
// Subtype of certificate to match
CertSubType string
}
const (
// Text marker indicating start of certificate in PEM format
CertificateBeginMarker = "-----BEGIN CERTIFICATE-----"
// Text marker indicating end of certificate in PEM format
CertificateEndMarker = "-----END CERTIFICATE-----"
// Maximum number of lines for a single certificate
CertificateMaxLines = 128
// Maximum number of certificates or known host entries in a stream
CertificateMaxEntriesPerStream = 256
// Local path where certificate data is stored
CertificateDataPath = "/app/config/tls"
)
// Decode a certificate in PEM format to X509 data structure
func DecodePEMCertificateToX509(pemData string) (*x509.Certificate, error) {
decodedData, _ := pem.Decode([]byte(pemData))
if decodedData == nil {
return nil, errors.New("Could not decode PEM data from input.")
}
x509Cert, err := x509.ParseCertificate(decodedData.Bytes)
if err != nil {
return nil, errors.New("Could not parse X509 data from input.")
}
return x509Cert, nil
}
// Parse TLS certificates from a multiline string
func ParseTLSCertificatesFromData(data string) ([]string, error) {
return ParseTLSCertificatesFromStream(strings.NewReader(data))
}
// Parse TLS certificates from a file
func ParseTLSCertificatesFromPath(sourceFile string) ([]string, error) {
fileHandle, err := os.Open(sourceFile)
if err != nil {
return nil, err
}
defer fileHandle.Close()
return ParseTLSCertificatesFromStream(fileHandle)
}
// Parse TLS certificate data from a data stream. The stream may contain more
// than one certificate. Each found certificate will generate a unique entry
// in the returned slice, so the length of the slice indicates how many
// certificates have been found.
func ParseTLSCertificatesFromStream(stream io.Reader) ([]string, error) {
scanner := bufio.NewScanner(stream)
inCertData := false
pemData := ""
curLine := 0
certLine := 0
certificateList := make([]string, 0)
// TODO: Implement maximum amount of data to parse
// TODO: Implement error heuristics
for scanner.Scan() {
curLine += 1
if !inCertData {
if strings.HasPrefix(scanner.Text(), CertificateBeginMarker) {
certLine = 1
inCertData = true
pemData += scanner.Text() + "\n"
}
} else {
certLine += 1
pemData += scanner.Text() + "\n"
if strings.HasPrefix(scanner.Text(), CertificateEndMarker) {
inCertData = false
certificateList = append(certificateList, pemData)
pemData = ""
}
}
if certLine > CertificateMaxLines {
return nil, errors.New("Maximum number of lines exceeded during certificate parsing.")
}
}
return certificateList, nil
}
// Parse TLS certificates from a multiline string
func ParseSSHKnownHostsFromData(data string) ([]string, error) {
return ParseSSHKnownHostsFromStream(strings.NewReader(data))
}
func ParseSSHKnownHostsFromPath(sourceFile string) ([]string, error) {
fileHandle, err := os.Open(sourceFile)
if err != nil {
return nil, err
}
defer fileHandle.Close()
return ParseSSHKnownHostsFromStream(fileHandle)
}
// Parses a list of strings in SSH's known host data format from a stream and
// returns the valid entries in an array.
func ParseSSHKnownHostsFromStream(stream io.Reader) ([]string, error) {
scanner := bufio.NewScanner(stream)
knownHostsLists := make([]string, 0)
curLine := 0
numEntries := 0
for scanner.Scan() {
curLine += 1
lineData := scanner.Text()
if IsValidSSHKnownHostsEntry(lineData) {
numEntries += 1
knownHostsLists = append(knownHostsLists, lineData)
}
}
return knownHostsLists, nil
}
// Checks whether we can use a line from ssh_known_hosts data as an actual data
// source for a RepoCertificate object. This function only checks for syntactic
// validity, not if the data in the line is valid.
func IsValidSSHKnownHostsEntry(line string) bool {
trimmedEntry := strings.TrimSpace(line)
// We ignore commented out lines - usually happens when copy and pasting
// to the ConfigMap from a known_hosts file or from ssh-keyscan output.
if len(trimmedEntry) == 0 || trimmedEntry[0] == '#' {
return false
}
// Each line should consist of three fields: host, type, data
keyData := strings.SplitN(trimmedEntry, " ", 3)
return len(keyData) == 3
}
// Tokenize a known_hosts entry into hostname, key sub type and actual key data
func TokenizeSSHKnownHostsEntry(knownHostsEntry string) (string, string, []byte, error) {
knownHostsToken := strings.SplitN(knownHostsEntry, " ", 3)
if len(knownHostsToken) != 3 {
return "", "", nil, fmt.Errorf("Error while tokenizing input data.")
}
return knownHostsToken[0], knownHostsToken[1], []byte(knownHostsToken[2]), nil
}
// Parse a raw known hosts line into a PublicKey object and a list of hosts the
// key would be valid for.
func KnownHostsLineToPublicKey(line string) ([]string, ssh.PublicKey, error) {
_, hostnames, keyData, _, _, err := ssh.ParseKnownHosts([]byte(line))
if err != nil {
return nil, nil, err
}
return hostnames, keyData, nil
}
func TokenizedDataToPublicKey(hostname string, subType string, rawKeyData string) ([]string, ssh.PublicKey, error) {
hostnames, keyData, err := KnownHostsLineToPublicKey(fmt.Sprintf("%s %s %s", hostname, subType, rawKeyData))
if err != nil {
return nil, nil, err
}
return hostnames, keyData, nil
}
// We do not use full fledged regular expression for matching the hostname.
// Instead, we use a less expensive file system glob, which should be fully
// sufficient for our use case.
func MatchHostName(hostname, pattern string) bool {
// If pattern is empty, we always return a match
if pattern == "" {
return true
}
match, err := filepath.Match(pattern, hostname)
if err != nil {
return false
}
return match
}
// base64 sha256 hash with the trailing equal sign removed
func SSHFingerprintSHA256(key ssh.PublicKey) string {
hash := sha256.Sum256(key.Marshal())
b64hash := base64.StdEncoding.EncodeToString(hash[:])
return strings.TrimRight(b64hash, "=")
}
// Load certificate data from a file. If the file does not exist, we do not
// consider it an error and just return empty data.
func GetCertificateForConnect(serverName string) ([]string, error) {
certPath := fmt.Sprintf("%s/%s", CertificateDataPath, serverName)
certificates, err := ParseTLSCertificatesFromPath(certPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
} else {
return nil, err
}
}
if len(certificates) == 0 {
return nil, fmt.Errorf("No certificates found in existing file.")
}
return certificates, nil
}
func GetCertBundlePathForRepository(serverName string) (string, error) {
certPath := fmt.Sprintf("%s/%s", CertificateDataPath, serverName)
certs, err := GetCertificateForConnect(serverName)
if err != nil {
return "", nil
}
if len(certs) == 0 {
return "", nil
}
return certPath, nil
}
func GetCertPoolFromPEMData(pemData []string) *x509.CertPool {
certPool := x509.NewCertPool()
for _, pem := range pemData {
certPool.AppendCertsFromPEM([]byte(pem))
}
return certPool
}

337
util/cert/cert_test.go Normal file
View file

@ -0,0 +1,337 @@
package cert
import (
"testing"
"github.com/stretchr/testify/assert"
)
const Test_Cert1CN = "CN=foo.example.com,OU=SpecOps,O=Capone\\, Inc,L=Chicago,ST=IL,C=US"
const Test_Cert2CN = "CN=bar.example.com,OU=Testsuite,O=Testing Corp,L=Hanover,ST=Lower Saxony,C=DE"
const Test_TLSValidSingleCert = `
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIUGrTmW3qc39zqnE08e3qNDhUkeWswDQYJKoZIhvcNAQEL
BQAwbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMB4XDTE5MDcwODEzNTUwNVoXDTIwMDcwNzEzNTUw
NVowbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA3csSO13w7qQXKeSLNcpeuAe6wAjXYbRkRl6ariqzTEDcFTKmy2QiXJTKoEGn
bvwxq0T91var7rxY88SGL/qi8Zmo0tVSR0XvKSKcghFIkQOTyDmVgMPZGCvixt4q
gQ7hUVSk4KkFmtcqBVuvnzI1d/DKfZAGKdmGcfRpuAsnVhac3swP0w4Tl1BFrK9U
vuIkz4KwXG77s5oB8rMUnyuLasLsGNpvpvXhkcQRhp6vpcCO2bS7kOTTelAPIucw
P37qkOEdZdiWCLrr57dmhg6tmcVlmBMg6JtmfLxn2HQd9ZrCKlkWxMk5NYs6CAW5
kgbDZUWQTAsnHeoJKbcgtPkIbxDRxNpPukFMtbA4VEWv1EkODXy9FyEKDOI/PV6K
/80oLkgCIhCkP2mvwSFheU0RHTuZ0o0vVolP5TEOq5iufnDN4wrxqb12o//XLRc0
RiLqGVVxhFdyKCjVxcLfII9AAp5Tse4PMh6bf6jDfB3OMvGkhMbJWhKXdR2NUTl0
esKawMPRXIn5g3oBdNm8kyRsTTnvB567pU8uNSmA8j3jxfGCPynI8JdiwKQuW/+P
WgLIflgxqAfG85dVVOsFmF9o5o24dDslvv9yHnHH102c6ijPCg1EobqlyFzqqxOD
Wf2OPjIkzoTH+O27VRugnY/maIU1nshNO7ViRX5zIxEUtNMCAwEAAaNTMFEwHQYD
VR0OBBYEFNY4gDLgPBidogkmpO8nq5yAq5g+MB8GA1UdIwQYMBaAFNY4gDLgPBid
ogkmpO8nq5yAq5g+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AJ0WGioNtGNg3m6ywpmxNThorQD5ZvDMlmZlDVk78E2wfNyMhwbVhKhlAnONv0wv
kmsGjibY75nRZ+EK9PxSJ644841fryQXQ+bli5fhr7DW3uTKwaRsnzETJXRJuljq
6+c6Zyg1/mqwnyx7YvPgVh3w496DYx/jm6Fm1IEq3BzOmn6H/gGPq3gbURzEqI3h
P+kC2vJa8RZWrpa05Xk/Q1QUkErDX9vJghb9z3+GgirISZQzqWRghII/znv3NOE6
zoIgaaWNFn8KPeBVpUoboH+IhpgibsnbTbI0G7AMtFq6qm3kn/4DZ2N2tuh1G2tT
zR2Fh7hJbU7CrqxANrgnIoHG/nLSvzE24ckLb0Vj69uGQlwnZkn9fz6F7KytU+Az
NoB2rjufaB0GQi1azdboMvdGSOxhSCAR8otWT5yDrywCqVnEvjw0oxKmuRduNe2/
6AcG6TtK2/K+LHuhymiAwZM2qE6VD2odvb+tCzDkZOIeoIz/JcVlNpXE9FuVl250
9NWvugeghq7tUv81iJ8ninBefJ4lUfxAehTPQqX+zXcfxgjvMRCi/ig73nLyhmjx
r2AaraPFgrprnxUibP4L7jxdr+iiw5bWN9/B81PodrS7n5TNtnfnpZD6X6rThqOP
xO7Tr5lAo74vNUkF2EHNaI28/RGnJPm2TIxZqy4rNH6L
-----END CERTIFICATE-----
`
const Test_TLSInvalidPEMData = `
MIIF1zCCA7+gAwIBAgIUQdTcSHY2Sxd3Tq/v1eIEZPCNbOowDQYJKoZIhvcNAQEL
BQAwezELMAkGA1UEBhMCREUxFTATBgNVBAgMDExvd2VyIFNheG9ueTEQMA4GA1UE
BwwHSGFub3ZlcjEVMBMGA1UECgwMVGVzdGluZyBDb3JwMRIwEAYDVQQLDAlUZXN0
c3VpdGUxGDAWBgNVBAMMD2Jhci5leGFtcGxlLmNvbTAeFw0xOTA3MDgxMzU2MTda
Fw0yMDA3MDcxMzU2MTdaMHsxCzAJBgNVBAYTAkRFMRUwEwYDVQQIDAxMb3dlciBT
YXhvbnkxEDAOBgNVBAcMB0hhbm92ZXIxFTATBgNVBAoMDFRlc3RpbmcgQ29ycDES
MBAGA1UECwwJVGVzdHN1aXRlMRgwFgYDVQQDDA9iYXIuZXhhbXBsZS5jb20wggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCv4mHMdVUcafmaSHVpUM0zZWp5
NFXfboxA4inuOkE8kZlbGSe7wiG9WqLirdr39Ts+WSAFA6oANvbzlu3JrEQ2CHPc
CNQm6diPREFwcDPFCe/eMawbwkQAPVSHPts0UoRxnpZox5pn69ghncBR+jtvx+/u
P6HdwW0qqTvfJnfAF1hBJ4oIk2AXiip5kkIznsAh9W6WRy6nTVCeetmIepDOGe0G
ZJIRn/OfSz7NzKylfDCat2z3EAutyeT/5oXZoWOmGg/8T7pn/pR588GoYYKRQnp+
YilqCPFX+az09EqqK/iHXnkdZ/Z2fCuU+9M/Zhrnlwlygl3RuVBI6xhm/ZsXtL2E
Gxa61lNy6pyx5+hSxHEFEJshXLtioRd702VdLKxEOuYSXKeJDs1x9o6cJ75S6hko
`
const Test_TLSInvalidSingleCert = `
-----BEGIN CERTIFICATE-----
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
XWyb96wrUlv+E8I=
-----END CERTIFICATE-----
`
const Test_TLSValidMultiCert = `
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIUGrTmW3qc39zqnE08e3qNDhUkeWswDQYJKoZIhvcNAQEL
BQAwbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMB4XDTE5MDcwODEzNTUwNVoXDTIwMDcwNzEzNTUw
NVowbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA3csSO13w7qQXKeSLNcpeuAe6wAjXYbRkRl6ariqzTEDcFTKmy2QiXJTKoEGn
bvwxq0T91var7rxY88SGL/qi8Zmo0tVSR0XvKSKcghFIkQOTyDmVgMPZGCvixt4q
gQ7hUVSk4KkFmtcqBVuvnzI1d/DKfZAGKdmGcfRpuAsnVhac3swP0w4Tl1BFrK9U
vuIkz4KwXG77s5oB8rMUnyuLasLsGNpvpvXhkcQRhp6vpcCO2bS7kOTTelAPIucw
P37qkOEdZdiWCLrr57dmhg6tmcVlmBMg6JtmfLxn2HQd9ZrCKlkWxMk5NYs6CAW5
kgbDZUWQTAsnHeoJKbcgtPkIbxDRxNpPukFMtbA4VEWv1EkODXy9FyEKDOI/PV6K
/80oLkgCIhCkP2mvwSFheU0RHTuZ0o0vVolP5TEOq5iufnDN4wrxqb12o//XLRc0
RiLqGVVxhFdyKCjVxcLfII9AAp5Tse4PMh6bf6jDfB3OMvGkhMbJWhKXdR2NUTl0
esKawMPRXIn5g3oBdNm8kyRsTTnvB567pU8uNSmA8j3jxfGCPynI8JdiwKQuW/+P
WgLIflgxqAfG85dVVOsFmF9o5o24dDslvv9yHnHH102c6ijPCg1EobqlyFzqqxOD
Wf2OPjIkzoTH+O27VRugnY/maIU1nshNO7ViRX5zIxEUtNMCAwEAAaNTMFEwHQYD
VR0OBBYEFNY4gDLgPBidogkmpO8nq5yAq5g+MB8GA1UdIwQYMBaAFNY4gDLgPBid
ogkmpO8nq5yAq5g+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AJ0WGioNtGNg3m6ywpmxNThorQD5ZvDMlmZlDVk78E2wfNyMhwbVhKhlAnONv0wv
kmsGjibY75nRZ+EK9PxSJ644841fryQXQ+bli5fhr7DW3uTKwaRsnzETJXRJuljq
6+c6Zyg1/mqwnyx7YvPgVh3w496DYx/jm6Fm1IEq3BzOmn6H/gGPq3gbURzEqI3h
P+kC2vJa8RZWrpa05Xk/Q1QUkErDX9vJghb9z3+GgirISZQzqWRghII/znv3NOE6
zoIgaaWNFn8KPeBVpUoboH+IhpgibsnbTbI0G7AMtFq6qm3kn/4DZ2N2tuh1G2tT
zR2Fh7hJbU7CrqxANrgnIoHG/nLSvzE24ckLb0Vj69uGQlwnZkn9fz6F7KytU+Az
NoB2rjufaB0GQi1azdboMvdGSOxhSCAR8otWT5yDrywCqVnEvjw0oxKmuRduNe2/
6AcG6TtK2/K+LHuhymiAwZM2qE6VD2odvb+tCzDkZOIeoIz/JcVlNpXE9FuVl250
9NWvugeghq7tUv81iJ8ninBefJ4lUfxAehTPQqX+zXcfxgjvMRCi/ig73nLyhmjx
r2AaraPFgrprnxUibP4L7jxdr+iiw5bWN9/B81PodrS7n5TNtnfnpZD6X6rThqOP
xO7Tr5lAo74vNUkF2EHNaI28/RGnJPm2TIxZqy4rNH6L
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
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
XWyb96wrUlv+E8I=
-----END CERTIFICATE-----
`
// Taken from hack/ssh_known_hosts
const Test_ValidSSHKnownHostsData = `
# BitBucket
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
# GitHub
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
# GitLab
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
# Azure
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
`
const Test_InvalidSSHKnownHostsData = `
bitbucket.org AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
# GitHub
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
# GitLab
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
# Azure
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
`
func Test_TLSCertificate_ValidPEM_ValidCert(t *testing.T) {
// Valid PEM data, single certificate, expect array of length 1
certificates, err := ParseTLSCertificatesFromData(Test_TLSValidSingleCert)
assert.Nil(t, err)
assert.Equal(t, len(certificates), 1)
// Expect good decode
x509Cert, err := DecodePEMCertificateToX509(certificates[0])
assert.Nil(t, err)
assert.Equal(t, x509Cert.Subject.String(), Test_Cert1CN)
}
func Test_TLSCertificate_ValidPEM_InvalidCert(t *testing.T) {
// Valid PEM data, but invalid certificate
certificates, err := ParseTLSCertificatesFromData(Test_TLSInvalidSingleCert)
assert.Nil(t, err)
assert.Equal(t, len(certificates), 1)
// Expect bad decode
_, err = DecodePEMCertificateToX509(certificates[0])
assert.NotNil(t, err)
}
func Test_TLSCertificate_InvalidPEM(t *testing.T) {
// Invalid PEM data, expect array of length 0
certificates, err := ParseTLSCertificatesFromData(Test_TLSInvalidPEMData)
assert.Nil(t, err)
assert.Equal(t, len(certificates), 0)
}
func Test_TLSCertificate_ValidPEM_ValidCert_Multi(t *testing.T) {
// Valid PEM data, two certificates, expect array of length 2
certificates, err := ParseTLSCertificatesFromData(Test_TLSValidMultiCert)
assert.Nil(t, err)
assert.Equal(t, len(certificates), 2)
// Expect good decode
x509Cert, err := DecodePEMCertificateToX509(certificates[0])
assert.Nil(t, err)
assert.Equal(t, x509Cert.Subject.String(), Test_Cert1CN)
x509Cert, err = DecodePEMCertificateToX509(certificates[1])
assert.Nil(t, err)
assert.Equal(t, x509Cert.Subject.String(), Test_Cert2CN)
}
func Test_TLSCertificate_ValidPEM_ValidCert_FromFile(t *testing.T) {
// Valid PEM data, single certificate from file, expect array of length 1
certificates, err := ParseTLSCertificatesFromPath("../../test/certificates/cert1.pem")
assert.Nil(t, err)
assert.Equal(t, len(certificates), 1)
// Expect good decode
x509Cert, err := DecodePEMCertificateToX509(certificates[0])
assert.Nil(t, err)
assert.Equal(t, x509Cert.Subject.String(), Test_Cert1CN)
}
func Test_TLSCertPool(t *testing.T) {
certificates, err := ParseTLSCertificatesFromData(Test_TLSValidMultiCert)
assert.Nil(t, err)
assert.Equal(t, len(certificates), 2)
certPool := GetCertPoolFromPEMData(certificates)
assert.NotNil(t, certPool)
}
func Test_TLSCertificate_CertFromNonExistingFile(t *testing.T) {
// Non-existing file, expect err
_, err := ParseTLSCertificatesFromPath("../../test/certificates/cert_nonexisting.pem")
assert.NotNil(t, err)
}
func Test_SSHKnownHostsData_ParseData(t *testing.T) {
// Expect valid data with 7 known host entries
entries, err := ParseSSHKnownHostsFromData(Test_ValidSSHKnownHostsData)
assert.Nil(t, err)
assert.Equal(t, len(entries), 7)
}
func Test_SSHKnownHostsData_ParseFile(t *testing.T) {
// Expect valid data with 7 known host entries
entries, err := ParseSSHKnownHostsFromPath("../../test/certificates/ssh_known_hosts")
assert.Nil(t, err)
assert.Equal(t, len(entries), 7)
}
func Test_SSHKnownHostsData_ParseNonExistingFile(t *testing.T) {
// Expect valid data with 7 known host entries
entries, err := ParseSSHKnownHostsFromPath("../../test/certificates/ssh_known_hosts_invalid")
assert.NotNil(t, err)
assert.Nil(t, entries)
}
func Test_SSHKnownHostsData_Tokenize(t *testing.T) {
// All entries should parse to valid SSH public keys
// All entries should be tokenizable, and tokens should be feedable to decoder
entries, err := ParseSSHKnownHostsFromData(Test_ValidSSHKnownHostsData)
assert.Nil(t, err)
for _, entry := range entries {
hosts, _, err := KnownHostsLineToPublicKey(entry)
assert.Nil(t, err)
assert.Equal(t, len(hosts), 1)
hoststring, subtype, certdata, err := TokenizeSSHKnownHostsEntry(entry)
assert.Nil(t, err)
hosts, _, err = TokenizedDataToPublicKey(hoststring, subtype, string(certdata))
assert.Nil(t, err)
assert.Equal(t, len(hosts), 1)
}
}
func Test_MatchHostName(t *testing.T) {
matchHostName := "foo.example.com"
assert.Equal(t, MatchHostName(matchHostName, "*"), true)
assert.Equal(t, MatchHostName(matchHostName, "*.example.com"), true)
assert.Equal(t, MatchHostName(matchHostName, "foo.*"), true)
assert.Equal(t, MatchHostName(matchHostName, "foo.*.com"), true)
assert.Equal(t, MatchHostName(matchHostName, "fo?.example.com"), true)
assert.Equal(t, MatchHostName(matchHostName, "foo?.example.com"), false)
assert.Equal(t, MatchHostName(matchHostName, "bar.example.com"), false)
assert.Equal(t, MatchHostName(matchHostName, "*.otherexample.com"), false)
assert.Equal(t, MatchHostName(matchHostName, "foo.otherexample.*"), false)
}
func Test_SSHFingerprintSHA256(t *testing.T) {
// actual SHA256 fingerprints for keys defined above
fingerprints := [...]string{
"zzXQOXSRBEiUtuE8AikJYKwbHaxvSc0ojez9YXaGp1A",
"nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8",
"HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw",
"eUXGGm1YGsMAS7vkcx6JOJdOGHPem5gQp4taiCfCLB8",
"ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ",
"ohD8VZEXGWo6Ez8GSEJQ9WpafgLFsOfLOtGGQCQo6Og",
"ohD8VZEXGWo6Ez8GSEJQ9WpafgLFsOfLOtGGQCQo6Og",
}
entries, err := ParseSSHKnownHostsFromData(Test_ValidSSHKnownHostsData)
assert.Nil(t, err)
assert.Equal(t, len(entries), 7)
for idx, entry := range entries {
_, pubKey, err := KnownHostsLineToPublicKey(entry)
assert.Nil(t, err)
fp := SSHFingerprintSHA256(pubKey)
assert.Equal(t, fp, fingerprints[idx])
}
}

453
util/db/certificate.go Normal file
View file

@ -0,0 +1,453 @@
package db
import (
"fmt"
"golang.org/x/crypto/ssh"
"golang.org/x/net/context"
"github.com/argoproj/argo-cd/common"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
certutil "github.com/argoproj/argo-cd/util/cert"
)
// A struct representing an entry in the list of SSH known hosts.
type SSHKnownHostsEntry struct {
// Hostname the key is for
Host string
// The type of the key
SubType string
// The data of the key, including the type
Data string
// The SHA256 fingerprint of the key
Fingerprint string
}
// A representation of a TLS certificate
type TLSCertificate struct {
// Subject of the certificate
Subject string
// Issuer of the certificate
Issuer string
// Certificate data
Data string
}
// Helper struct for certificate selection
type CertificateListSelector struct {
// Pattern to match the hostname with
HostNamePattern string
// Type of certificate to match
CertType string
// Subtype of certificate to match
CertSubType string
}
// Get a list of all configured repository certificates matching the given
// selector.
func (db *db) ListRepoCertificates(ctx context.Context, selector *CertificateListSelector) (*appsv1.RepositoryCertificateList, error) {
// selector may be given as nil, but we need at least an empty data structure
// so we create it if necessary.
if selector == nil {
selector = &CertificateListSelector{}
}
certificates := make([]appsv1.RepositoryCertificate, 0)
// Get all SSH known host entries
if selector.CertType == "" || selector.CertType == "*" || selector.CertType == "ssh" {
sshKnownHosts, err := db.getSSHKnownHostsData()
if err != nil {
return nil, err
}
for _, entry := range sshKnownHosts {
if certutil.MatchHostName(entry.Host, selector.HostNamePattern) && (selector.CertSubType == "" || selector.CertSubType == "*" || selector.CertSubType == entry.SubType) {
certificates = append(certificates, appsv1.RepositoryCertificate{
ServerName: entry.Host,
CertType: "ssh",
CertSubType: entry.SubType,
CertData: []byte(entry.Data),
CertFingerprint: entry.Fingerprint,
})
}
}
}
// Get all TLS certificates
if selector.CertType == "" || selector.CertType == "*" || selector.CertType == "https" || selector.CertType == "tls" {
tlsCertificates, err := db.getTLSCertificateData()
if err != nil {
return nil, err
}
for _, entry := range tlsCertificates {
if certutil.MatchHostName(entry.Subject, selector.HostNamePattern) {
pemEntries, err := certutil.ParseTLSCertificatesFromData(entry.Data)
if err != nil {
continue
}
for _, pemEntry := range pemEntries {
certificates = append(certificates, appsv1.RepositoryCertificate{
ServerName: entry.Subject,
CertType: "https",
CertData: []byte(pemEntry),
})
}
}
}
}
return &appsv1.RepositoryCertificateList{
Items: certificates,
}, nil
}
// Get a single certificate from the datastore
func (db *db) GetRepoCertificate(ctx context.Context, serverType string, serverName string) (*appsv1.RepositoryCertificate, error) {
if serverType == "ssh" {
sshKnownHostsList, err := db.getSSHKnownHostsData()
if err != nil {
return nil, err
}
for _, entry := range sshKnownHostsList {
if entry.Host == serverName {
repo := &appsv1.RepositoryCertificate{
ServerName: entry.Host,
CertType: "ssh",
CertSubType: entry.SubType,
CertData: []byte(entry.Data),
CertFingerprint: entry.Fingerprint,
}
return repo, nil
}
}
}
// Fail
return nil, nil
}
// Create one or more repository certificates and returns a list of certificates
// actually created.
func (db *db) CreateRepoCertificate(ctx context.Context, certificates *appsv1.RepositoryCertificateList, upsert bool) (*appsv1.RepositoryCertificateList, error) {
var (
saveSSHData bool = false
saveTLSData bool = false
)
sshKnownHostsList, err := db.getSSHKnownHostsData()
if err != nil {
return nil, err
}
tlsCertificates, err := db.getTLSCertificateData()
if err != nil {
return nil, err
}
// This will hold the final list of certificates that have been created
created := make([]appsv1.RepositoryCertificate, 0)
// Each request can contain multiple certificates of different types, so we
// make sure to handle each request accordingly.
for _, certificate := range certificates.Items {
if certificate.CertType == "ssh" {
// Whether we have a new certificate entry
newEntry := true
// Whether we have upserted an existing certificate entry
upserted := false
// Check whether known hosts entry already exists. Must match hostname
// and the key sub type (e.g. ssh-rsa). It is considered an error if we
// already have a corresponding key and upsert was not specified.
for _, entry := range sshKnownHostsList {
if entry.Host == certificate.ServerName && entry.SubType == certificate.CertSubType {
if !upsert && entry.Data != string(certificate.CertData) {
return nil, fmt.Errorf("Key for '%s' (subtype: '%s') already exist and upsert was not specified.", entry.Host, entry.SubType)
} else {
// Do not add an entry on upsert, but remember if we actual did an
// upsert.
newEntry = false
if entry.Data != string(certificate.CertData) {
entry.Data = string(certificate.CertData)
upserted = true
}
break
}
}
}
// Make sure that we received a valid public host key by parsing it
_, _, rawKeyData, _, _, err := ssh.ParseKnownHosts([]byte(fmt.Sprintf("%s %s %s", certificate.ServerName, certificate.CertSubType, certificate.CertData)))
if err != nil {
return nil, err
}
if newEntry {
sshKnownHostsList = append(sshKnownHostsList, &SSHKnownHostsEntry{
Host: certificate.ServerName,
Data: string(certificate.CertData),
SubType: certificate.CertSubType,
})
}
// If we created a new entry, or if we upserted an existing one, we need
// to save the data and notify the consumer about the operation.
if newEntry || upserted {
certificate.CertFingerprint = certutil.SSHFingerprintSHA256(rawKeyData)
created = append(created, certificate)
saveSSHData = true
}
} else if certificate.CertType == "https" {
var tlsCertificate *TLSCertificate = nil
newEntry := true
upserted := false
pemCreated := make([]string, 0)
for _, entry := range tlsCertificates {
// We have an entry for this server already. Check for upsert.
if entry.Subject == certificate.ServerName {
newEntry = false
if entry.Data != string(certificate.CertData) {
if !upsert {
return nil, fmt.Errorf("TLS certificate for server '%s' already exist and upsert was not specified.", entry.Subject)
}
}
// Store pointer to this entry for later use.
tlsCertificate = entry
break
}
}
// Check for validity of data received
pemData, err := certutil.ParseTLSCertificatesFromData(string(certificate.CertData))
if err != nil {
return nil, err
}
// We should have at least one valid PEM entry
if len(pemData) == 0 {
return nil, fmt.Errorf("No valid PEM data received.")
}
// Make sure we have valid X509 certificates in the data
for _, entry := range pemData {
_, err := certutil.DecodePEMCertificateToX509(entry)
if err != nil {
return nil, err
}
pemCreated = append(pemCreated, entry)
}
// New certificate if pointer to existing cert is nil
if tlsCertificate == nil {
tlsCertificate = &TLSCertificate{
Subject: certificate.ServerName,
Data: string(certificate.CertData),
}
tlsCertificates = append(tlsCertificates, tlsCertificate)
} else {
// We have made sure the upsert flag was set above. Now just figure out
// again if we have to actually update the data in the existing cert.
if tlsCertificate.Data != string(certificate.CertData) {
tlsCertificate.Data = string(certificate.CertData)
upserted = true
}
}
if newEntry || upserted {
// We append the certificate for every PEM entry in the request, so the
// caller knows that we processed each single item.
for _, entry := range pemCreated {
created = append(created, appsv1.RepositoryCertificate{
ServerName: certificate.ServerName,
CertType: "https",
CertData: []byte(entry),
})
}
saveTLSData = true
}
} else {
// Invalid/unknown certificate type
return nil, fmt.Errorf("Unknown certificate type: %s", certificate.CertType)
}
}
if saveSSHData {
err = db.settingsMgr.SaveSSHKnownHostsData(ctx, knownHostsDataToStrings(sshKnownHostsList))
if err != nil {
return nil, err
}
}
if saveTLSData {
err = db.settingsMgr.SaveTLSCertificateData(ctx, tlsCertificatesToMap(tlsCertificates))
if err != nil {
return nil, err
}
}
return &appsv1.RepositoryCertificateList{Items: created}, nil
}
// Batch remove configured certificates according to the selector query
func (db *db) RemoveRepoCertificates(ctx context.Context, selector *CertificateListSelector) (*appsv1.RepositoryCertificateList, error) {
var (
knownHostsOld []*SSHKnownHostsEntry
knownHostsNew []*SSHKnownHostsEntry
tlsCertificatesOld []*TLSCertificate
tlsCertificatesNew []*TLSCertificate
err error
)
removed := &appsv1.RepositoryCertificateList{
Items: make([]appsv1.RepositoryCertificate, 0),
}
if selector.CertType == "" || selector.CertType == "ssh" || selector.CertType == "*" {
knownHostsOld, err = db.getSSHKnownHostsData()
if err != nil {
return nil, err
}
knownHostsNew = make([]*SSHKnownHostsEntry, 0)
for _, entry := range knownHostsOld {
if matchSSHKnownHostsEntry(entry, selector) {
removed.Items = append(removed.Items, appsv1.RepositoryCertificate{
ServerName: entry.Host,
CertType: "ssh",
CertSubType: entry.SubType,
CertData: []byte(entry.Data),
})
} else {
knownHostsNew = append(knownHostsNew, entry)
}
}
}
if selector.CertType == "" || selector.CertType == "*" || selector.CertType == "https" || selector.CertType == "tls" {
tlsCertificatesOld, err = db.getTLSCertificateData()
if err != nil {
return nil, err
}
tlsCertificatesNew = make([]*TLSCertificate, 0)
for _, entry := range tlsCertificatesOld {
if certutil.MatchHostName(entry.Subject, selector.HostNamePattern) {
// Wrap each PEM certificate into its own RepositoryCertificate object
// so the caller knows what has been removed actually.
//
// The downside of this is, only valid data can be removed from the CM,
// so if the data somehow got corrupted, it can only be removed by
// means of editing the CM directly using e.g. kubectl.
pemCertificates, err := certutil.ParseTLSCertificatesFromData(entry.Data)
if err != nil {
return nil, err
}
if len(pemCertificates) > 0 {
for _, pem := range pemCertificates {
removed.Items = append(removed.Items, appsv1.RepositoryCertificate{
ServerName: entry.Subject,
CertType: "https",
CertData: []byte(pem),
})
}
}
} else {
tlsCertificatesNew = append(tlsCertificatesNew, entry)
}
}
}
if len(knownHostsNew) < len(knownHostsOld) {
err = db.settingsMgr.SaveSSHKnownHostsData(ctx, knownHostsDataToStrings(knownHostsNew))
if err != nil {
return nil, err
}
}
if len(tlsCertificatesNew) < len(tlsCertificatesOld) {
err = db.settingsMgr.SaveTLSCertificateData(ctx, tlsCertificatesToMap(tlsCertificatesNew))
if err != nil {
return nil, err
}
}
return removed, nil
}
// Converts list of known hosts data to array of strings, suitable for storing
// in a known_hosts file for SSH.
func knownHostsDataToStrings(knownHostsList []*SSHKnownHostsEntry) []string {
knownHostsData := make([]string, 0)
for _, entry := range knownHostsList {
knownHostsData = append(knownHostsData, fmt.Sprintf("%s %s %s", entry.Host, entry.SubType, entry.Data))
}
return knownHostsData
}
// Converts list of TLS certificates to a map whose key will be the certificate
// subject and the data will be a string containing TLS certificate data as PEM
func tlsCertificatesToMap(tlsCertificates []*TLSCertificate) map[string]string {
certMap := make(map[string]string)
for _, entry := range tlsCertificates {
certMap[entry.Subject] = entry.Data
}
return certMap
}
// Get the TLS certificate data from the config map
func (db *db) getTLSCertificateData() ([]*TLSCertificate, error) {
certificates := make([]*TLSCertificate, 0)
certCM, err := db.settingsMgr.GetNamedConfigMap(common.ArgoCDTLSCertsConfigMapName)
if err != nil {
return nil, err
}
for key, entry := range certCM.Data {
certificates = append(certificates, &TLSCertificate{Subject: key, Data: entry})
}
return certificates, nil
}
// Gets the SSH known host data from ConfigMap and parse it into an array of
// SSHKnownHostEntry structs.
func (db *db) getSSHKnownHostsData() ([]*SSHKnownHostsEntry, error) {
certCM, err := db.settingsMgr.GetNamedConfigMap(common.ArgoCDKnownHostsConfigMapName)
if err != nil {
return nil, err
}
sshKnownHostsData := certCM.Data["ssh_known_hosts"]
entries := make([]*SSHKnownHostsEntry, 0)
// ssh_known_hosts data contains one key per line, so we must iterate over
// the whole data to get all keys.
//
// We validate the data found to a certain extent before we accept them as
// entry into our list to be returned.
//
sshKnownHostsEntries, err := certutil.ParseSSHKnownHostsFromData(sshKnownHostsData)
if err != nil {
return nil, err
}
for _, entry := range sshKnownHostsEntries {
hostname, subType, keyData, err := certutil.TokenizeSSHKnownHostsEntry(entry)
if err != nil {
return nil, err
}
entries = append(entries, &SSHKnownHostsEntry{
Host: hostname,
SubType: subType,
Data: string(keyData),
})
}
return entries, nil
}
func matchSSHKnownHostsEntry(entry *SSHKnownHostsEntry, selector *CertificateListSelector) bool {
return certutil.MatchHostName(entry.Host, selector.HostNamePattern) && (selector.CertSubType == "" || selector.CertSubType == "*" || selector.CertSubType == entry.SubType)
}

736
util/db/certificate_test.go Normal file
View file

@ -0,0 +1,736 @@
package db
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/settings"
)
const Test_Cert1CN = "CN=foo.example.com,OU=SpecOps,O=Capone\\, Inc,L=Chicago,ST=IL,C=US"
const Test_Cert2CN = "CN=bar.example.com,OU=Testsuite,O=Testing Corp,L=Hanover,ST=Lower Saxony,C=DE"
var Test_TLS_Subjects []string = []string{
"CN=foo.example.com,OU=SpecOps,O=Capone\\, Inc,L=Chicago,ST=IL,C=US",
"CN=bar.example.com,OU=Testsuite,O=Testing Corp,L=Hanover,ST=Lower Saxony,C=DE",
}
const Test_TLSValidSingleCert = `
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIUGrTmW3qc39zqnE08e3qNDhUkeWswDQYJKoZIhvcNAQEL
BQAwbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMB4XDTE5MDcwODEzNTUwNVoXDTIwMDcwNzEzNTUw
NVowbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA3csSO13w7qQXKeSLNcpeuAe6wAjXYbRkRl6ariqzTEDcFTKmy2QiXJTKoEGn
bvwxq0T91var7rxY88SGL/qi8Zmo0tVSR0XvKSKcghFIkQOTyDmVgMPZGCvixt4q
gQ7hUVSk4KkFmtcqBVuvnzI1d/DKfZAGKdmGcfRpuAsnVhac3swP0w4Tl1BFrK9U
vuIkz4KwXG77s5oB8rMUnyuLasLsGNpvpvXhkcQRhp6vpcCO2bS7kOTTelAPIucw
P37qkOEdZdiWCLrr57dmhg6tmcVlmBMg6JtmfLxn2HQd9ZrCKlkWxMk5NYs6CAW5
kgbDZUWQTAsnHeoJKbcgtPkIbxDRxNpPukFMtbA4VEWv1EkODXy9FyEKDOI/PV6K
/80oLkgCIhCkP2mvwSFheU0RHTuZ0o0vVolP5TEOq5iufnDN4wrxqb12o//XLRc0
RiLqGVVxhFdyKCjVxcLfII9AAp5Tse4PMh6bf6jDfB3OMvGkhMbJWhKXdR2NUTl0
esKawMPRXIn5g3oBdNm8kyRsTTnvB567pU8uNSmA8j3jxfGCPynI8JdiwKQuW/+P
WgLIflgxqAfG85dVVOsFmF9o5o24dDslvv9yHnHH102c6ijPCg1EobqlyFzqqxOD
Wf2OPjIkzoTH+O27VRugnY/maIU1nshNO7ViRX5zIxEUtNMCAwEAAaNTMFEwHQYD
VR0OBBYEFNY4gDLgPBidogkmpO8nq5yAq5g+MB8GA1UdIwQYMBaAFNY4gDLgPBid
ogkmpO8nq5yAq5g+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AJ0WGioNtGNg3m6ywpmxNThorQD5ZvDMlmZlDVk78E2wfNyMhwbVhKhlAnONv0wv
kmsGjibY75nRZ+EK9PxSJ644841fryQXQ+bli5fhr7DW3uTKwaRsnzETJXRJuljq
6+c6Zyg1/mqwnyx7YvPgVh3w496DYx/jm6Fm1IEq3BzOmn6H/gGPq3gbURzEqI3h
P+kC2vJa8RZWrpa05Xk/Q1QUkErDX9vJghb9z3+GgirISZQzqWRghII/znv3NOE6
zoIgaaWNFn8KPeBVpUoboH+IhpgibsnbTbI0G7AMtFq6qm3kn/4DZ2N2tuh1G2tT
zR2Fh7hJbU7CrqxANrgnIoHG/nLSvzE24ckLb0Vj69uGQlwnZkn9fz6F7KytU+Az
NoB2rjufaB0GQi1azdboMvdGSOxhSCAR8otWT5yDrywCqVnEvjw0oxKmuRduNe2/
6AcG6TtK2/K+LHuhymiAwZM2qE6VD2odvb+tCzDkZOIeoIz/JcVlNpXE9FuVl250
9NWvugeghq7tUv81iJ8ninBefJ4lUfxAehTPQqX+zXcfxgjvMRCi/ig73nLyhmjx
r2AaraPFgrprnxUibP4L7jxdr+iiw5bWN9/B81PodrS7n5TNtnfnpZD6X6rThqOP
xO7Tr5lAo74vNUkF2EHNaI28/RGnJPm2TIxZqy4rNH6L
-----END CERTIFICATE-----
`
const Test_TLSInvalidPEMData = `
MIIF1zCCA7+gAwIBAgIUQdTcSHY2Sxd3Tq/v1eIEZPCNbOowDQYJKoZIhvcNAQEL
BQAwezELMAkGA1UEBhMCREUxFTATBgNVBAgMDExvd2VyIFNheG9ueTEQMA4GA1UE
BwwHSGFub3ZlcjEVMBMGA1UECgwMVGVzdGluZyBDb3JwMRIwEAYDVQQLDAlUZXN0
c3VpdGUxGDAWBgNVBAMMD2Jhci5leGFtcGxlLmNvbTAeFw0xOTA3MDgxMzU2MTda
Fw0yMDA3MDcxMzU2MTdaMHsxCzAJBgNVBAYTAkRFMRUwEwYDVQQIDAxMb3dlciBT
YXhvbnkxEDAOBgNVBAcMB0hhbm92ZXIxFTATBgNVBAoMDFRlc3RpbmcgQ29ycDES
MBAGA1UECwwJVGVzdHN1aXRlMRgwFgYDVQQDDA9iYXIuZXhhbXBsZS5jb20wggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCv4mHMdVUcafmaSHVpUM0zZWp5
NFXfboxA4inuOkE8kZlbGSe7wiG9WqLirdr39Ts+WSAFA6oANvbzlu3JrEQ2CHPc
CNQm6diPREFwcDPFCe/eMawbwkQAPVSHPts0UoRxnpZox5pn69ghncBR+jtvx+/u
P6HdwW0qqTvfJnfAF1hBJ4oIk2AXiip5kkIznsAh9W6WRy6nTVCeetmIepDOGe0G
ZJIRn/OfSz7NzKylfDCat2z3EAutyeT/5oXZoWOmGg/8T7pn/pR588GoYYKRQnp+
YilqCPFX+az09EqqK/iHXnkdZ/Z2fCuU+9M/Zhrnlwlygl3RuVBI6xhm/ZsXtL2E
Gxa61lNy6pyx5+hSxHEFEJshXLtioRd702VdLKxEOuYSXKeJDs1x9o6cJ75S6hko
`
const Test_TLSInvalidSingleCert = `
-----BEGIN CERTIFICATE-----
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
XWyb96wrUlv+E8I=
-----END CERTIFICATE-----
`
const Test_TLSValidMultiCert = `
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIUGrTmW3qc39zqnE08e3qNDhUkeWswDQYJKoZIhvcNAQEL
BQAwbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMB4XDTE5MDcwODEzNTUwNVoXDTIwMDcwNzEzNTUw
NVowbjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAklMMRAwDgYDVQQHDAdDaGljYWdv
MRQwEgYDVQQKDAtDYXBvbmUsIEluYzEQMA4GA1UECwwHU3BlY09wczEYMBYGA1UE
AwwPZm9vLmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA3csSO13w7qQXKeSLNcpeuAe6wAjXYbRkRl6ariqzTEDcFTKmy2QiXJTKoEGn
bvwxq0T91var7rxY88SGL/qi8Zmo0tVSR0XvKSKcghFIkQOTyDmVgMPZGCvixt4q
gQ7hUVSk4KkFmtcqBVuvnzI1d/DKfZAGKdmGcfRpuAsnVhac3swP0w4Tl1BFrK9U
vuIkz4KwXG77s5oB8rMUnyuLasLsGNpvpvXhkcQRhp6vpcCO2bS7kOTTelAPIucw
P37qkOEdZdiWCLrr57dmhg6tmcVlmBMg6JtmfLxn2HQd9ZrCKlkWxMk5NYs6CAW5
kgbDZUWQTAsnHeoJKbcgtPkIbxDRxNpPukFMtbA4VEWv1EkODXy9FyEKDOI/PV6K
/80oLkgCIhCkP2mvwSFheU0RHTuZ0o0vVolP5TEOq5iufnDN4wrxqb12o//XLRc0
RiLqGVVxhFdyKCjVxcLfII9AAp5Tse4PMh6bf6jDfB3OMvGkhMbJWhKXdR2NUTl0
esKawMPRXIn5g3oBdNm8kyRsTTnvB567pU8uNSmA8j3jxfGCPynI8JdiwKQuW/+P
WgLIflgxqAfG85dVVOsFmF9o5o24dDslvv9yHnHH102c6ijPCg1EobqlyFzqqxOD
Wf2OPjIkzoTH+O27VRugnY/maIU1nshNO7ViRX5zIxEUtNMCAwEAAaNTMFEwHQYD
VR0OBBYEFNY4gDLgPBidogkmpO8nq5yAq5g+MB8GA1UdIwQYMBaAFNY4gDLgPBid
ogkmpO8nq5yAq5g+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AJ0WGioNtGNg3m6ywpmxNThorQD5ZvDMlmZlDVk78E2wfNyMhwbVhKhlAnONv0wv
kmsGjibY75nRZ+EK9PxSJ644841fryQXQ+bli5fhr7DW3uTKwaRsnzETJXRJuljq
6+c6Zyg1/mqwnyx7YvPgVh3w496DYx/jm6Fm1IEq3BzOmn6H/gGPq3gbURzEqI3h
P+kC2vJa8RZWrpa05Xk/Q1QUkErDX9vJghb9z3+GgirISZQzqWRghII/znv3NOE6
zoIgaaWNFn8KPeBVpUoboH+IhpgibsnbTbI0G7AMtFq6qm3kn/4DZ2N2tuh1G2tT
zR2Fh7hJbU7CrqxANrgnIoHG/nLSvzE24ckLb0Vj69uGQlwnZkn9fz6F7KytU+Az
NoB2rjufaB0GQi1azdboMvdGSOxhSCAR8otWT5yDrywCqVnEvjw0oxKmuRduNe2/
6AcG6TtK2/K+LHuhymiAwZM2qE6VD2odvb+tCzDkZOIeoIz/JcVlNpXE9FuVl250
9NWvugeghq7tUv81iJ8ninBefJ4lUfxAehTPQqX+zXcfxgjvMRCi/ig73nLyhmjx
r2AaraPFgrprnxUibP4L7jxdr+iiw5bWN9/B81PodrS7n5TNtnfnpZD6X6rThqOP
xO7Tr5lAo74vNUkF2EHNaI28/RGnJPm2TIxZqy4rNH6L
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
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
XWyb96wrUlv+E8I=
-----END CERTIFICATE-----
`
// Taken from hack/ssh_known_hosts
const Test_ValidSSHKnownHostsData = `
# BitBucket
bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
# GitHub
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
# GitLab
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
# Azure
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
`
const Test_InvalidSSHKnownHostsData = `
bitbucket.org AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==
# GitHub
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
# GitLab
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
# Azure
ssh.dev.azure.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
vs-ssh.visualstudio.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7Hr1oTWqNqOlzGJOfGJ4NakVyIzf1rXYd4d7wo6jBlkLvCA4odBlL0mDUyZ0/QUfTTqeu+tm22gOsv+VrVTMk6vwRU75gY/y9ut5Mb3bR5BV58dKXyq9A9UeB5Cakehn5Zgm6x1mKoVyf+FFn26iYqXJRgzIZZcZ5V6hrE0Qg39kZm4az48o0AUbf6Sp4SLdvnuMa2sVNwHBboS7EJkm57XQPVU3/QpyNLHbWDdzwtrlS+ez30S3AdYhLKEOxAG8weOnyrtLJAUen9mTkol8oII1edf7mWWbWVf0nBmly21+nZcmCTISQBtdcyPaEno7fFQMDD26/s0lfKob4Kw8H
`
var Test_SSH_Hostname_Entries []string = []string{
"bitbucket.org",
"github.com",
"gitlab.com",
"gitlab.com",
"gitlab.com",
"ssh.dev.azure.com",
"vs-ssh.visualstudio.com",
}
var Test_SSH_Subtypes []string = []string{
"ssh-rsa",
"ssh-rsa",
"ecdsa-sha2-nistp256",
"ssh-ed25519",
"ssh-rsa",
"ssh-rsa",
"ssh-rsa",
}
var Test_TLS_Hostnames []string = []string{
"test.example.com",
"test.example.com",
"github.com",
}
const Test_NumSSHKnownHostsExpected = 7
const Test_NumTLSCertificatesExpected = 3
func getCertClientset() *fake.Clientset {
cm := v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-cm",
Namespace: testNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: nil,
}
sshCM := v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-ssh-known-hosts-cm",
Namespace: testNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"ssh_known_hosts": Test_ValidSSHKnownHostsData,
},
}
tlsCM := v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-tls-certs-cm",
Namespace: testNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"test.example.com": Test_TLSValidMultiCert,
"gitlab.com": Test_TLSValidSingleCert,
},
}
return fake.NewSimpleClientset([]runtime.Object{&cm, &sshCM, &tlsCM}...)
}
func Test_ListCertificate(t *testing.T) {
clientset := getCertClientset()
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
assert.NotNil(t, db)
// List all SSH known host entries from configuration.
// Expected: List of 7 entries
certList, err := db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "*",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumSSHKnownHostsExpected)
for idx, entry := range certList.Items {
assert.Equal(t, entry.ServerName, Test_SSH_Hostname_Entries[idx])
assert.Equal(t, entry.CertSubType, Test_SSH_Subtypes[idx])
}
// List all TLS certificates from configuration.
// Expected: List of 3 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "*",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumTLSCertificatesExpected)
// List all certificates using selector
// Expected: List of 10 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "*",
CertType: "*",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumTLSCertificatesExpected+Test_NumSSHKnownHostsExpected)
// List all certificates using nil selector
// Expected: List of 10 entries
certList, err = db.ListRepoCertificates(context.Background(), nil)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, len(certList.Items), Test_NumTLSCertificatesExpected+Test_NumSSHKnownHostsExpected)
// List all certificates matching a host name pattern
// Expected: List of 4 entries, all with servername gitlab.com
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "gitlab.com",
CertType: "*",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 4, len(certList.Items))
for _, entry := range certList.Items {
assert.Equal(t, "gitlab.com", entry.ServerName)
}
// List all TLS certificates matching a host name pattern
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "gitlab.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
assert.Equal(t, "gitlab.com", certList.Items[0].ServerName)
assert.Equal(t, "https", certList.Items[0].CertType)
}
func Test_CreateSSHKnownHostEntries(t *testing.T) {
clientset := getCertClientset()
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
assert.NotNil(t, db)
// Valid known hosts entry
certList, err := db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "ssh",
CertData: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDioSMcGxdVkHaQzRjP71nY4mgVHXjuZiYN9NBiUxNZ0DYGjTIENI3uV45XxrS6PQfoyekUlVlHK2jwpcPrqAg6rlAdMD5WIxzvCnFjCuPA6Ljk8p0ZmYbvriDcgtj+UfGEdyUTgxH2gch6KwTY0eAbLue15IuXtoNzpLxk29iGRi5ZXNAbSBjeB3hm2PKLa6LnDqdkvc+nqoYqn1Fvx7ZJIh0apBCJpOtHPON4rnl7QQvNg9pWulZ5GKcpYMRfTpvHyFTEyrsVT5GH38l9s355GqU7GxQ/i6Tj1D0MKrIB2WmdjOnujM/ELLsrkYspMhn8ZRpCphN/LTcrOWsb0AM69drvYlhc6cnNAtC4UXp0GUy1HsBiJCsUm9/1Gz23VLDRvWop8yE8+PE3Ho5eL7ad9wmOG0mSOYEqVvAstmd8vzbD6oRuY8qV8X3tt9ph2tMAve0Qbo0NN3c51c9OfdXtJaSyckjEjaK7zjnArnYfladZZVlf2Tv8FsV0sJmfSAE="),
},
},
}, false)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Check if it really was added
// Result: List of 1 entry
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "foo.example.com",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Existing cert, same data, no upsert
// Result: no error, should return 0 added certificates
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "ssh",
CertData: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDioSMcGxdVkHaQzRjP71nY4mgVHXjuZiYN9NBiUxNZ0DYGjTIENI3uV45XxrS6PQfoyekUlVlHK2jwpcPrqAg6rlAdMD5WIxzvCnFjCuPA6Ljk8p0ZmYbvriDcgtj+UfGEdyUTgxH2gch6KwTY0eAbLue15IuXtoNzpLxk29iGRi5ZXNAbSBjeB3hm2PKLa6LnDqdkvc+nqoYqn1Fvx7ZJIh0apBCJpOtHPON4rnl7QQvNg9pWulZ5GKcpYMRfTpvHyFTEyrsVT5GH38l9s355GqU7GxQ/i6Tj1D0MKrIB2WmdjOnujM/ELLsrkYspMhn8ZRpCphN/LTcrOWsb0AM69drvYlhc6cnNAtC4UXp0GUy1HsBiJCsUm9/1Gz23VLDRvWop8yE8+PE3Ho5eL7ad9wmOG0mSOYEqVvAstmd8vzbD6oRuY8qV8X3tt9ph2tMAve0Qbo0NN3c51c9OfdXtJaSyckjEjaK7zjnArnYfladZZVlf2Tv8FsV0sJmfSAE="),
},
},
}, false)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
// Existing cert, different data, no upsert
// Result: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "ssh",
CertData: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=="),
},
},
}, false)
assert.NotNil(t, err)
assert.Nil(t, certList)
// Existing cert, different data, upsert
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "ssh",
CertData: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=="),
},
},
}, true)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Invalid known hosts entry, case 1: key sub type missing
// Result: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "bar.example.com",
CertType: "ssh",
CertData: []byte("AAAAB3NzaC1yc2EAAAADAQABAAABgQDioSMcGxdVkHaQzRjP71nY4mgVHXjuZiYN9NBiUxNZ0DYGjTIENI3uV45XxrS6PQfoyekUlVlHK2jwpcPrqAg6rlAdMD5WIxzvCnFjCuPA6Ljk8p0ZmYbvriDcgtj+UfGEdyUTgxH2gch6KwTY0eAbLue15IuXtoNzpLxk29iGRi5ZXNAbSBjeB3hm2PKLa6LnDqdkvc+nqoYqn1Fvx7ZJIh0apBCJpOtHPON4rnl7QQvNg9pWulZ5GKcpYMRfTpvHyFTEyrsVT5GH38l9s355GqU7GxQ/i6Tj1D0MKrIB2WmdjOnujM/ELLsrkYspMhn8ZRpCphN/LTcrOWsb0AM69drvYlhc6cnNAtC4UXp0GUy1HsBiJCsUm9/1Gz23VLDRvWop8yE8+PE3Ho5eL7ad9wmOG0mSOYEqVvAstmd8vzbD6oRuY8qV8X3tt9ph2tMAve0Qbo0NN3c51c9OfdXtJaSyckjEjaK7zjnArnYfladZZVlf2Tv8FsV0sJmfSAE="),
},
},
}, false)
assert.NotNil(t, err)
assert.Nil(t, certList)
// Invalid known hosts entry, case 2: invalid base64 data
// Result: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "bar.example.com",
CertType: "ssh",
CertData: []byte("ssh-rsa AAAAB3Nza1yc2EAAAADAQABAAABgQDioSMcGxdVkHaQzRjP71nY4mgVHXjuZiYN9NBiUxNZ0DYGjTIENI3uV45XxrS6PQfoyekUlVlHK2jwpcPrqAg6rlAdMD5WIxzvCnFjCuPA6Ljk8p0ZmYbvriDcgtj+UfGEdyUTgxH2gch6KwTY0eAbLue15IuXtoNzpLxk29iGRi5ZXNAbSBjeB3hm2PKLa6LnDqdkvc+nqoYqn1Fvx7ZJIh0apBCJpOtHPON4rnl7QQvNg9pWulZ5GKcpYMRfTpvHyFTEyrsVT5GH38l9s355GqU7GxQ/i6Tj1D0MKrIB2WmdjOnujM/ELLsrkYspMhn8ZRpCphN/LTcrOWsb0AM69drvYlhc6cnNAtC4UXp0GUy1HsBiJCsUm9/1Gz23VLDRvWop8yE8+PE3Ho5eL7ad9wmOG0mSOYEqVvAstmd8vzbD6oRuY8qV8X3tt9ph2tMAve0Qbo0NN3c51c9OfdXtJaSyckjEjaK7zjnArnYfladZZVlf2Tv8FsV0sJmfSAE="),
},
},
}, false)
assert.NotNil(t, err)
assert.Nil(t, certList)
}
func Test_CreateTLSCertificates(t *testing.T) {
clientset := getCertClientset()
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
assert.NotNil(t, db)
// Valid TLS certificate
// Expected: List of 1 entry
certList, err := db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "https",
CertData: []byte(Test_TLSValidSingleCert),
},
},
}, false)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Check if it really was added
// Result: Return new certificate
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "foo.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Valid TLS certificates, multiple PEMs in data
// Expected: List of 2 entry
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "bar.example.com",
CertType: "https",
CertData: []byte(Test_TLSValidMultiCert),
},
},
}, false)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
// Check if it really was added
// Result: Return new certificate
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "bar.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
// Valid TLS certificate, existing cert, same data, no upsert
// Expected: List of 0 entry
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "https",
CertData: []byte(Test_TLSValidSingleCert),
},
},
}, false)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
// Valid TLS certificate, existing cert, different data, no upsert
// Expected: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "https",
CertData: []byte(Test_TLSValidMultiCert),
},
},
}, false)
assert.NotNil(t, err)
assert.Nil(t, certList)
// Valid TLS certificate, existing cert, different data, upsert
// Expected: List of 2 entries
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "foo.example.com",
CertType: "https",
CertData: []byte(Test_TLSValidMultiCert),
},
},
}, true)
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
// Check if upsert was successful
// Expected: List of 2 entries, matching hostnames & cert types
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "foo.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
for _, entry := range certList.Items {
assert.Equal(t, "foo.example.com", entry.ServerName)
assert.Equal(t, "https", entry.CertType)
}
// Invalid PEM data, new cert
// Expected: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "baz.example.com",
CertType: "https",
CertData: []byte(Test_TLSInvalidPEMData),
},
},
}, false)
assert.NotNil(t, err)
assert.Nil(t, certList)
// Valid PEM data, new cert, but invalid certificate
// Expected: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "baz.example.com",
CertType: "https",
CertData: []byte(Test_TLSInvalidSingleCert),
},
},
}, false)
assert.NotNil(t, err)
assert.Nil(t, certList)
// Invalid PEM data, existing cert, upsert
// Expected: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "baz.example.com",
CertType: "https",
CertData: []byte(Test_TLSInvalidPEMData),
},
},
}, true)
assert.NotNil(t, err)
assert.Nil(t, certList)
// Valid PEM data, existing cert, but invalid certificate, upsert
// Expected: Error
certList, err = db.CreateRepoCertificate(context.Background(), &v1alpha1.RepositoryCertificateList{
Items: []v1alpha1.RepositoryCertificate{
{
ServerName: "baz.example.com",
CertType: "https",
CertData: []byte(Test_TLSInvalidSingleCert),
},
},
}, true)
assert.NotNil(t, err)
assert.Nil(t, certList)
}
func Test_RemoveSSHKnownHosts(t *testing.T) {
clientset := getCertClientset()
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
assert.NotNil(t, db)
// Remove single SSH known hosts entry by hostname
// Expected: List of 1 entry
certList, err := db.RemoveRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "github.com",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Check whether entry was really removed
// Expected: List of 0 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "github.com",
CertType: "ssh",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
// Remove single SSH known hosts entry by sub type
// Expected: List of 1 entry
certList, err = db.RemoveRepoCertificates(context.Background(), &CertificateListSelector{
CertType: "ssh",
CertSubType: "ssh-ed25519",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Check whether entry was really removed
// Expected: List of 0 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
CertType: "ssh",
CertSubType: "ssh-ed25519",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
// Remove all remaining SSH known hosts entries
// Expected: List of 5 entry
certList, err = db.RemoveRepoCertificates(context.Background(), &CertificateListSelector{
CertType: "ssh",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 5, len(certList.Items))
// Check whether the entries were really removed
// Expected: List of 0 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
CertType: "ssh",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
}
func Test_RemoveTLSCertificates(t *testing.T) {
clientset := getCertClientset()
db := NewDB(testNamespace, settings.NewSettingsManager(context.Background(), clientset, testNamespace), clientset)
assert.NotNil(t, db)
// Remove single TLS certificate entry by hostname
// Expected: List of 1 entry
certList, err := db.RemoveRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "gitlab.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 1, len(certList.Items))
// Check whether entry was really removed
// Expected: List of 0 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "gitlab.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
// Remove all TLS certificate entry for hostname
// Expected: List of 2 entry
certList, err = db.RemoveRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "test.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 2, len(certList.Items))
// Check whether entries were really removed
// Expected: List of 0 entries
certList, err = db.ListRepoCertificates(context.Background(), &CertificateListSelector{
HostNamePattern: "test.example.com",
CertType: "https",
})
assert.Nil(t, err)
assert.NotNil(t, certList)
assert.Equal(t, 0, len(certList.Items))
}

View file

@ -37,6 +37,13 @@ type ArgoDB interface {
// ListHelmRepoURLs lists configured helm repositories
ListHelmRepos(ctx context.Context) ([]*appv1.HelmRepository, error)
// ListRepoCerticifates lists all configured certificates
ListRepoCertificates(ctx context.Context, selector *CertificateListSelector) (*appv1.RepositoryCertificateList, error)
// CreateRepoCertificate creates a new certificate entry
CreateRepoCertificate(ctx context.Context, certificate *appv1.RepositoryCertificateList, upsert bool) (*appv1.RepositoryCertificateList, error)
// CreateRepoCertificate creates a new certificate entry
RemoveRepoCertificates(ctx context.Context, selector *CertificateListSelector) (*appv1.RepositoryCertificateList, error)
}
type db struct {

View file

@ -38,6 +38,9 @@ func getClientset(config map[string]string, objects ...runtime.Object) *fake.Cli
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-cm",
Namespace: testNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: config,
}

View file

@ -64,7 +64,8 @@ func (db *db) CreateRepository(ctx context.Context, r *appsv1.Repository) (*apps
repoInfo := settings.RepoCredentials{
URL: r.Repo,
InsecureIgnoreHostKey: r.InsecureIgnoreHostKey,
InsecureIgnoreHostKey: (r.InsecureIgnoreHostKey || r.Insecure),
Insecure: (r.InsecureIgnoreHostKey || r.Insecure),
}
err = db.updateSecrets(&repoInfo, r)
if err != nil {
@ -121,6 +122,7 @@ func (db *db) credentialsToRepository(repoInfo settings.RepoCredentials) (*appsv
repo := &appsv1.Repository{
Repo: repoInfo.URL,
InsecureIgnoreHostKey: repoInfo.InsecureIgnoreHostKey,
Insecure: repoInfo.Insecure,
}
err := db.unmarshalFromSecretsStr(map[*string]*apiv1.SecretKeySelector{
&repo.Username: repoInfo.UsernameSecret,

View file

@ -1,7 +1,10 @@
package git
import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"strconv"
@ -15,10 +18,11 @@ import (
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
ssh2 "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
"gopkg.in/src-d/go-git.v4/storage/memory"
certutil "github.com/argoproj/argo-cd/util/cert"
argoconfig "github.com/argoproj/argo-cd/util/config"
)
@ -44,14 +48,19 @@ type Client interface {
// ClientFactory is a factory of Git Clients
// Primarily used to support creation of mock git clients during unit testing
type ClientFactory interface {
NewClient(repoURL, path, username, password, sshPrivateKey string, insecureIgnoreHostKey bool) (Client, error)
NewClient(repoURL, path, username, password, sshPrivateKey string, insecure bool) (Client, error)
}
// nativeGitClient implements Client interface using git CLI
type nativeGitClient struct {
// URL of the repository
repoURL string
root string
creds Creds
// Root path of repository
root string
// Authenticator credentials for private repositories
creds Creds
// Whether to connect insecurely to repository, e.g. don't verify certificate
insecure bool
}
type factory struct{}
@ -60,23 +69,90 @@ func NewFactory() ClientFactory {
return &factory{}
}
func (f *factory) NewClient(rawRepoURL, path, username, password, sshPrivateKey string, insecureIgnoreHostKey bool) (Client, error) {
func (f *factory) NewClient(rawRepoURL, path, username, password, sshPrivateKey string, insecure bool) (Client, error) {
var creds Creds
if sshPrivateKey != "" {
creds = SSHCreds{sshPrivateKey, insecureIgnoreHostKey}
creds = SSHCreds{sshPrivateKey, insecure}
} else if username != "" || password != "" {
creds = HTTPSCreds{username, password}
} else {
creds = NopCreds{}
}
// We need a custom HTTP client for go-git when we want to skip validation
// of the server's TLS certificate (--insecure-ignore-server-cert). Since
// this change is permanent to go-git Client during runtime, we need to
// explicitly replace it with default client for repositories without the
// insecure flag set.
//if IsHTTPSURL(rawRepoURL) {
// gitclient.InstallProtocol("https", githttp.NewClient(getRepoHTTPClient(rawRepoURL, insecure)))
//}
client := nativeGitClient{
repoURL: rawRepoURL,
root: path,
creds: creds,
repoURL: rawRepoURL,
root: path,
creds: creds,
insecure: insecure,
}
return &client, nil
}
// Returns a HTTP client object suitable for go-git to use using the following
// pattern:
// - If insecure is true, always returns a client with certificate verification
// turned off.
// - If one or more custom certificates are stored for the repository, returns
// a client with those certificates in the list of root CAs used to verify
// the server's certificate.
// - Otherwise (and on non-fatal errors), a default HTTP client is returned.
func getRepoHTTPClient(repoURL string, insecure bool) transport.Transport {
// Default HTTP client
var customHTTPClient transport.Transport = githttp.NewClient(&http.Client{})
if insecure {
customHTTPClient = githttp.NewClient(&http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
// 15 second timeout
Timeout: 15 * time.Second,
// don't follow redirect
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
})
} else {
parsedURL, err := url.Parse(repoURL)
if err != nil {
return customHTTPClient
}
serverCertificatePem, err := certutil.GetCertificateForConnect(parsedURL.Host)
if err != nil {
return customHTTPClient
} else if len(serverCertificatePem) > 0 {
certPool := certutil.GetCertPoolFromPEMData(serverCertificatePem)
customHTTPClient = githttp.NewClient(&http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: certPool,
},
},
// 15 second timeout
Timeout: 15 * time.Second,
// don't follow redirect
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
})
}
// else no custom certificate stored.
}
return customHTTPClient
}
func newAuth(repoURL string, creds Creds) (transport.AuthMethod, error) {
switch creds := creds.(type) {
case SSHCreds:
@ -94,7 +170,7 @@ func newAuth(repoURL string, creds Creds) (transport.AuthMethod, error) {
}
return auth, nil
case HTTPSCreds:
auth := http.BasicAuth{Username: creds.username, Password: creds.password}
auth := githttp.BasicAuth{Username: creds.username, Password: creds.password}
return &auth, nil
}
return nil, nil
@ -188,7 +264,8 @@ func (m *nativeGitClient) LsRemote(revision string) (string, error) {
if err != nil {
return "", err
}
refs, err := remote.List(&git.ListOptions{Auth: auth})
//refs, err := remote.List(&git.ListOptions{Auth: auth})
refs, err := listRemote(remote, &git.ListOptions{Auth: auth}, m.insecure)
if err != nil {
return "", err
}
@ -291,11 +368,30 @@ func (m *nativeGitClient) runCredentialedCmd(command string, args ...string) (st
}
func (m *nativeGitClient) runCmdOutput(cmd *exec.Cmd) (string, error) {
log.Debug(strings.Join(cmd.Args, " "))
cmd.Dir = m.root
cmd.Env = append(cmd.Env, os.Environ()...)
cmd.Env = append(cmd.Env, "HOME=/dev/null")
cmd.Env = append(cmd.Env, "GIT_CONFIG_NOSYSTEM=true")
cmd.Env = append(cmd.Env, "GIT_CONFIG_NOGLOBAL=true")
// For HTTPS repositories, we need to consider insecure repositories as well
// as custom CA bundles from the cert database.
if IsHTTPSURL(m.repoURL) {
if m.insecure {
cmd.Env = append(cmd.Env, "GIT_SSL_NO_VERIFY=true")
} else {
parsedURL, err := url.Parse(m.repoURL)
// We don't fail if we cannot parse the URL, but log a warning in that
// case. And we execute the command in a verbatim way.
if err != nil {
log.Warnf("runCmdOutput: Could not parse repo URL '%s'", m.repoURL)
} else {
caPath, err := certutil.GetCertBundlePathForRepository(parsedURL.Host)
if err == nil && caPath != "" {
cmd.Env = append(cmd.Env, fmt.Sprintf("GIT_SSL_CAINFO=%s", caPath))
}
}
}
}
log.Debug(strings.Join(cmd.Args, " "))
return argoexec.RunCommandExt(cmd, argoconfig.CmdOpts())
}

View file

@ -25,6 +25,7 @@ func removeSuffix(s, suffix string) string {
var (
commitSHARegex = regexp.MustCompile("^[0-9A-Fa-f]{40}$")
sshURLRegex = regexp.MustCompile("^(ssh://)?([^/@:]*?)@.*")
httpsURLRegex = regexp.MustCompile("^(https://).*")
)
// IsCommitSHA returns whether or not a string is a 40 character SHA-1
@ -71,6 +72,11 @@ func IsSSHURL(url string) (bool, string) {
return false, ""
}
// IsHTTPSURL returns true if supplied URL is HTTPS URL
func IsHTTPSURL(url string) bool {
return httpsURLRegex.MatchString(url)
}
// TestRepo tests if a repo exists and is accessible with the given credentials
func TestRepo(repo, username, password string, sshPrivateKey string, insecureIgnoreHostKey bool) error {
clnt, err := NewFactory().NewClient(repo, "", username, password, sshPrivateKey, insecureIgnoreHostKey)

74
util/git/workaround.go Normal file
View file

@ -0,0 +1,74 @@
package git
import (
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
"gopkg.in/src-d/go-git.v4/utils/ioutil"
)
// Below is a workaround for https://github.com/src-d/go-git/issues/1177: the `github.com/src-d/go-git` does not support disable SSL cert verification is a single repo.
// As workaround methods `newUploadPackSession`, `newClient` and `listRemote` were copied from https://github.com/src-d/go-git/blob/master/remote.go and modified to use
// transport with InsecureSkipVerify flag is verification should be disabled.
func newUploadPackSession(url string, auth transport.AuthMethod, insecureSkipTLSVerify bool) (transport.UploadPackSession, error) {
c, ep, err := newClient(url, insecureSkipTLSVerify)
if err != nil {
return nil, err
}
return c.NewUploadPackSession(ep, auth)
}
func newClient(url string, insecureSkipTLSVerify bool) (transport.Transport, *transport.Endpoint, error) {
ep, err := transport.NewEndpoint(url)
if err != nil {
return nil, nil, err
}
var c transport.Transport
// For HTTPS repositories, we get a custom Transport. Everything else will
// be default.
if IsHTTPSURL(url) {
c = getRepoHTTPClient(url, insecureSkipTLSVerify)
} else {
c, err = client.NewClient(ep)
if err != nil {
return nil, nil, err
}
}
return c, ep, err
}
func listRemote(r *git.Remote, o *git.ListOptions, insecureSkipTLSVerify bool) (rfs []*plumbing.Reference, err error) {
s, err := newUploadPackSession(r.Config().URLs[0], o.Auth, insecureSkipTLSVerify)
if err != nil {
return nil, err
}
defer ioutil.CheckClose(s, &err)
ar, err := s.AdvertisedReferences()
if err != nil {
return nil, err
}
allRefs, err := ar.AllReferences()
if err != nil {
return nil, err
}
refs, err := allRefs.IterReferences()
if err != nil {
return nil, err
}
var resultRefs []*plumbing.Reference
_ = refs.ForEach(func(ref *plumbing.Reference) error {
resultRefs = append(resultRefs, ref)
return nil
})
return resultRefs, nil
}

View file

@ -27,6 +27,9 @@ func TestSessionManager(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "argocd-cm",
Namespace: "argocd",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
}, &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{

View file

@ -80,12 +80,20 @@ type OIDCConfig struct {
RequestedScopes []string `json:"requestedScopes,omitempty"`
}
// Credentials for accessing a Git repository
type RepoCredentials struct {
URL string `json:"url,omitempty"`
UsernameSecret *apiv1.SecretKeySelector `json:"usernameSecret,omitempty"`
PasswordSecret *apiv1.SecretKeySelector `json:"passwordSecret,omitempty"`
SSHPrivateKeySecret *apiv1.SecretKeySelector `json:"sshPrivateKeySecret,omitempty"`
InsecureIgnoreHostKey bool `json:"insecureIgnoreHostKey,omitempty"`
// The URL to the repository
URL string `json:"url,omitempty"`
// Name of the secret storing the username used to access the repo
UsernameSecret *apiv1.SecretKeySelector `json:"usernameSecret,omitempty"`
// Name of the secret storing the password used to access the repo
PasswordSecret *apiv1.SecretKeySelector `json:"passwordSecret,omitempty"`
// Name of the secret storing the SSH private key used to access the repo
SSHPrivateKeySecret *apiv1.SecretKeySelector `json:"sshPrivateKeySecret,omitempty"`
// Whether to connect the repository in an insecure way (deprecated)
InsecureIgnoreHostKey bool `json:"insecureIgnoreHostKey,omitempty"`
// Whether to connect the repository in an insecure way
Insecure bool `json:"insecure,omitempty"`
}
type HelmRepoCredentials struct {
@ -196,6 +204,21 @@ func (mgr *SettingsManager) getConfigMap() (*apiv1.ConfigMap, error) {
return argoCDCM, err
}
// Returns the ConfigMap with the given name from the cluster.
// The ConfigMap must be labeled with "app.kubernetes.io/part-of: argocd" in
// order to be retrievable.
func (mgr *SettingsManager) GetNamedConfigMap(configMapName string) (*apiv1.ConfigMap, error) {
err := mgr.ensureSynced(false)
if err != nil {
return nil, err
}
configMap, err := mgr.configmaps.ConfigMaps(mgr.namespace).Get(configMapName)
if err != nil {
return nil, err
}
return configMap, err
}
func (mgr *SettingsManager) GetResourcesFilter() (*ResourcesFilter, error) {
argoCDCM, err := mgr.getConfigMap()
if err != nil {
@ -372,8 +395,9 @@ func (mgr *SettingsManager) GetSettings() (*ArgoCDSettings, error) {
func (mgr *SettingsManager) initialize(ctx context.Context) error {
tweakConfigMap := func(options *metav1.ListOptions) {
cmFieldSelector := fields.ParseSelectorOrDie(fmt.Sprintf("metadata.name=%s", common.ArgoCDConfigMapName))
options.FieldSelector = cmFieldSelector.String()
//cmFieldSelector := fields.ParseSelectorOrDie(fmt.Sprintf("metadata.name=%s", common.ArgoCDConfigMapName))
cmLabelSelector := fields.ParseSelectorOrDie("app.kubernetes.io/part-of=argocd")
options.LabelSelector = cmLabelSelector.String()
}
cmInformer := v1.NewFilteredConfigMapInformer(mgr.clientset, mgr.namespace, 3*time.Minute, cache.Indexers{}, tweakConfigMap)
@ -616,6 +640,51 @@ func (mgr *SettingsManager) SaveSettings(settings *ArgoCDSettings) error {
return mgr.ResyncInformers()
}
// Save the SSH known host data into the corresponding ConfigMap
func (mgr *SettingsManager) SaveSSHKnownHostsData(ctx context.Context, knownHostsList []string) error {
err := mgr.ensureSynced(false)
if err != nil {
return err
}
certCM, err := mgr.GetNamedConfigMap(common.ArgoCDKnownHostsConfigMapName)
if err != nil {
return err
}
if certCM.Data == nil {
certCM.Data = make(map[string]string)
}
certCM.Data["ssh_known_hosts"] = strings.Join(knownHostsList, "\n")
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Update(certCM)
if err != nil {
return nil
}
return mgr.ResyncInformers()
}
func (mgr *SettingsManager) SaveTLSCertificateData(ctx context.Context, tlsCertificates map[string]string) error {
err := mgr.ensureSynced(false)
if err != nil {
return err
}
certCM, err := mgr.GetNamedConfigMap(common.ArgoCDTLSCertsConfigMapName)
if err != nil {
return err
}
certCM.Data = tlsCertificates
_, err = mgr.clientset.CoreV1().ConfigMaps(mgr.namespace).Update(certCM)
if err != nil {
return nil
}
return mgr.ResyncInformers()
}
// NewSettingsManager generates a new SettingsManager pointer and returns it
func NewSettingsManager(ctx context.Context, clientset kubernetes.Interface, namespace string) *SettingsManager {

View file

@ -18,6 +18,9 @@ func TestGetRepositories(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"repositories": "\n - url: http://foo\n",
@ -34,6 +37,9 @@ func TestSaveRepositories(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
})
settingsManager := NewSettingsManager(context.Background(), kubeClient, "default")
@ -49,6 +55,9 @@ func TestGetRepositoryCredentials(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"repository.credentials": "\n - url: http://foo\n",
@ -65,6 +74,9 @@ func TestGetResourceFilter(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"resource.exclusions": "\n - apiGroups: [\"group1\"]\n kinds: [\"kind1\"]\n clusters: [\"cluster1\"]\n",
@ -85,6 +97,9 @@ func TestGetConfigManagementPlugins(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"configManagementPlugins": `
@ -110,6 +125,9 @@ func TestGetAppInstanceLabelKey(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"application.instanceLabelKey": "testLabel",
@ -126,6 +144,9 @@ func TestGetResourceOverrides(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"resource.customizations": `
@ -152,6 +173,9 @@ func TestGetHelmRepositories(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"helm.repositories": "\n - url: http://foo\n",
@ -169,6 +193,9 @@ func TestGetGoogleAnalytics(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: "default",
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
Data: map[string]string{
"ga.trackingid": "123",