Skip to content

Commit fb79fbf

Browse files
feat: update ssl cert on proxy for custom ssl (#1057) (#1077)
(cherry picked from commit c214355) Co-authored-by: Tanmoy Sarkar <57363826+tanmoysrt@users.noreply.github.com>
1 parent f93f884 commit fb79fbf

File tree

6 files changed

+146
-7
lines changed

6 files changed

+146
-7
lines changed

swiftwave_service/graphql/domain.resolvers.go

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

swiftwave_service/graphql/helpers.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,24 @@ package graphql
22

33
import (
44
"context"
5+
"encoding/pem"
56
"errors"
67
"fmt"
8+
"log"
9+
"os"
10+
"os/user"
11+
"path/filepath"
12+
"strings"
13+
714
containermanger "github.com/swiftwave-org/swiftwave/container_manager"
815
dockerconfiggenerator "github.com/swiftwave-org/swiftwave/docker_config_generator"
916
haproxymanager "github.com/swiftwave-org/swiftwave/haproxy_manager"
1017
"github.com/swiftwave-org/swiftwave/swiftwave_service/core"
1118
"github.com/swiftwave-org/swiftwave/swiftwave_service/graphql/model"
1219
"github.com/swiftwave-org/swiftwave/swiftwave_service/logger"
1320
"github.com/swiftwave-org/swiftwave/swiftwave_service/manager"
21+
"golang.org/x/crypto/ssh"
1422
"gorm.io/gorm"
15-
"log"
16-
"os"
17-
"os/user"
18-
"path/filepath"
19-
"strings"
2023
)
2124

2225
func convertMapToDockerConfigBuildArgs(input map[string]dockerconfiggenerator.Variable) []*model.DockerConfigBuildArg {
@@ -52,6 +55,37 @@ func sanitizeFileName(fileName string) string {
5255
return fileName
5356
}
5457

58+
func ValidateSSLFullChainCertificate(certString string) error {
59+
// Parse the SSL public key (including certificates)
60+
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certString))
61+
if err != nil {
62+
return fmt.Errorf("failed to parse SSL public key: %v", err)
63+
}
64+
65+
// Check if it's an SSH certificate
66+
_, ok := pubKey.(*ssh.Certificate)
67+
if !ok {
68+
return fmt.Errorf("provided file is not an SSL certificate")
69+
}
70+
71+
return nil
72+
}
73+
74+
func ValidateSSLPrivateKey(privateKeyString string) error {
75+
// Decode the PEM block
76+
block, _ := pem.Decode([]byte(privateKeyString))
77+
if block == nil {
78+
return fmt.Errorf("failed to decode PEM block from private key")
79+
}
80+
81+
// Try to parse the key as an SSH private key
82+
_, err := ssh.ParseRawPrivateKey(block.Bytes)
83+
if err != nil {
84+
return fmt.Errorf("failed to parse SSL private key: %v", err)
85+
}
86+
return nil
87+
}
88+
5589
func FetchDockerManager(ctx context.Context, db *gorm.DB) (*containermanger.Manager, error) {
5690
// Fetch a random swarm manager
5791
swarmManagerServer, err := core.FetchSwarmManager(db)

swiftwave_service/worker/init.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func (m Manager) registerWorkerFunctions() {
4040
panicOnError(taskQueueClient.RegisterFunction(redirectRuleApplyQueueName, m.RedirectRuleApply))
4141
panicOnError(taskQueueClient.RegisterFunction(redirectRuleDeleteQueueName, m.RedirectRuleDelete))
4242
panicOnError(taskQueueClient.RegisterFunction(sslGenerateQueueName, m.SSLGenerate))
43+
panicOnError(taskQueueClient.RegisterFunction(sslProxyUpdateQueueName, m.SSLProxyUpdate))
4344
panicOnError(taskQueueClient.RegisterFunction(deletePersistentVolumeQueueName, m.PersistentVolumeDeletion))
4445
panicOnError(taskQueueClient.RegisterFunction(persistentVolumeBackupQueueName, m.PersistentVolumeBackup))
4546
panicOnError(taskQueueClient.RegisterFunction(persistentVolumeRestoreQueueName, m.PersistentVolumeRestore))

swiftwave_service/worker/process_ssl_request.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import (
77
"crypto/x509"
88
"encoding/pem"
99
"errors"
10+
"log"
11+
"strings"
12+
1013
haproxymanager "github.com/swiftwave-org/swiftwave/haproxy_manager"
1114
"github.com/swiftwave-org/swiftwave/swiftwave_service/core"
1215
"github.com/swiftwave-org/swiftwave/swiftwave_service/manager"
1316
"gorm.io/gorm"
14-
"log"
1517
)
1618

1719
func (m Manager) SSLGenerate(request SSLGenerateRequest, ctx context.Context, _ context.CancelFunc) error {
@@ -120,6 +122,77 @@ func (m Manager) SSLGenerate(request SSLGenerateRequest, ctx context.Context, _
120122
return nil
121123
}
122124

125+
func (m Manager) SSLProxyUpdate(request SSLProxyUpdateRequest, ctx context.Context, _ context.CancelFunc) error {
126+
dbWithoutTx := m.ServiceManager.DbClient
127+
// fetch domain
128+
var domain core.Domain
129+
err := domain.FindById(ctx, dbWithoutTx, request.DomainId)
130+
if err != nil {
131+
if errors.Is(err, gorm.ErrRecordNotFound) {
132+
// if not found, return nil as no queue is required
133+
return nil
134+
}
135+
return err
136+
}
137+
// If domain is IPv4, don't generate SSL
138+
if domain.IsIPv4() {
139+
return nil
140+
}
141+
// If domain doesn't have SSL, return
142+
if domain.SSLStatus == core.DomainSSLStatusNone || strings.Compare(domain.SSLFullChain, "") == 0 || strings.Compare(domain.SSLPrivateKey, "") == 0 {
143+
return nil
144+
}
145+
// fetch all proxy servers
146+
proxyServers, err := core.FetchProxyActiveServers(&m.ServiceManager.DbClient)
147+
if err != nil {
148+
return err
149+
}
150+
// fetch all haproxy managers
151+
haproxyManagers, err := manager.HAProxyClients(context.Background(), proxyServers)
152+
if err != nil {
153+
return err
154+
}
155+
// map of server ip and transaction id
156+
transactionIdMap := make(map[*haproxymanager.Manager]string)
157+
isFailed := false
158+
159+
for _, haproxyManager := range haproxyManagers {
160+
// generate a new transaction id for haproxy
161+
transactionId, err := haproxyManager.FetchNewTransactionId()
162+
if err != nil {
163+
return err
164+
}
165+
// add to map
166+
transactionIdMap[haproxyManager] = transactionId
167+
// upload certificate to haproxy
168+
err = haproxyManager.UpdateSSL(transactionId, domain.Name, []byte(domain.SSLPrivateKey), []byte(domain.SSLFullChain))
169+
if err != nil {
170+
isFailed = true
171+
break
172+
}
173+
}
174+
for haproxyManager, haproxyTransactionId := range transactionIdMap {
175+
if !isFailed {
176+
// commit the haproxy transaction
177+
err = haproxyManager.CommitTransaction(haproxyTransactionId)
178+
}
179+
if isFailed || err != nil {
180+
isFailed = true
181+
log.Println("failed to commit haproxy transaction", err)
182+
err := haproxyManager.DeleteTransaction(haproxyTransactionId)
183+
if err != nil {
184+
log.Println("failed to rollback haproxy transaction", err)
185+
}
186+
}
187+
}
188+
189+
if isFailed {
190+
return domain.UpdateSSLStatus(ctx, dbWithoutTx, core.DomainSSLStatusFailed)
191+
} else {
192+
return domain.UpdateSSLStatus(ctx, dbWithoutTx, core.DomainSSLStatusIssued)
193+
}
194+
}
195+
123196
// private functions
124197
func generatePrivateKey() (string, error) {
125198
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)

swiftwave_service/worker/publisher.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ func (m Manager) EnqueueSSLGenerateRequest(domainId uint) error {
3535
})
3636
}
3737

38+
func (m Manager) EnqueueSSLProxyUpdateRequest(domainId uint) error {
39+
return m.ServiceManager.TaskQueueClient.EnqueueTask(sslProxyUpdateQueueName, SSLProxyUpdateRequest{
40+
DomainId: domainId,
41+
})
42+
}
43+
3844
func (m Manager) EnqueueIngressRuleApplyRequest(ingressRuleId uint) error {
3945
return m.ServiceManager.TaskQueueClient.EnqueueTask(ingressRuleApplyQueueName, IngressRuleApplyRequest{
4046
Id: ingressRuleId,

swiftwave_service/worker/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const (
2222
redirectRuleApplyQueueName = "redirect_rule_apply"
2323
redirectRuleDeleteQueueName = "redirect_rule_delete"
2424
sslGenerateQueueName = "ssl_generate"
25+
sslProxyUpdateQueueName = "ssl_update_proxy"
2526
persistentVolumeBackupQueueName = "persistent_volume_backup"
2627
persistentVolumeRestoreQueueName = "persistent_volume_restore"
2728
installDependenciesOnServerQueueName = "install_dependencies_on_server"
@@ -77,6 +78,11 @@ type SSLGenerateRequest struct {
7778
DomainId uint `json:"domain_id"`
7879
}
7980

81+
// SSLProxyUpdateRequest : request payload for updatin SSL ceritifcate on proxies
82+
type SSLProxyUpdateRequest struct {
83+
DomainId uint `json:"domain_id"`
84+
}
85+
8086
// DeleteApplicationRequest : request payload for application delete
8187
type DeleteApplicationRequest struct {
8288
Id string `json:"id"`

0 commit comments

Comments
 (0)