Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow delete balancers #9

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions data/balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"crypto/sha1"
"crypto/x509"
"encoding/pem"

"os"
"github.com/boltdb/bolt"
"gopkg.in/mgo.v2/bson"
)
Expand All @@ -23,17 +23,23 @@ type BalancerSettings struct {
Protocol Protocol
Algorithm Algorithm
SSLOptions SSLOptions
SSLVerifyClient SSLVerifyClient
}

type SSLOptions struct {
CipherSuite CipherSuite
Certificate []byte
PrivateKey []byte

SSLVerify SSLVerify
DNSNames []string
Fingerprint []byte
}

type SSLVerifyClient struct {

ClientCertificate []byte
}

func ListBalancers() ([]Balancer, error) {
bals := []Balancer{}
err := DB.View(func(tx *bolt.Tx) error {
Expand Down Expand Up @@ -76,6 +82,30 @@ func GetBalancer(id bson.ObjectId) (*Balancer, error) {
return bal, nil
}

func (b *Balancer) Delete() error {
servers, err := b.Servers()
if err != nil {
return err
}
for _, server := range servers {
err := server.Delete()
if err != nil {
return err
}
}

dirPath := "/var/lib/loadcat/out/" + b.Id.Hex()
err = os.RemoveAll(dirPath)
if err != nil {
return err
}

return DB.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("balancers"))
return bucket.Delete([]byte(b.Id.Hex()))
})
}

func (l *Balancer) Servers() ([]Server, error) {
return ListServersByBalancer(l)
}
Expand Down Expand Up @@ -111,6 +141,8 @@ func (l *Balancer) Put() error {
l.Settings.SSLOptions.PrivateKey = nil
l.Settings.SSLOptions.DNSNames = nil
l.Settings.SSLOptions.Fingerprint = nil
l.Settings.SSLVerifyClient.ClientCertificate = nil
l.Settings.SSLOptions.SSLVerify = "off"
}
return DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("balancers"))
Expand Down
7 changes: 7 additions & 0 deletions data/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ func (s *Server) Balancer() (*Balancer, error) {
return GetBalancer(s.BalancerId)
}

func (s *Server) Delete() error {
return DB.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("servers"))
return b.Delete([]byte(s.Id.Hex()))
})
}

func (s *Server) Put() error {
if !s.Id.Valid() {
s.Id = bson.NewObjectId()
Expand Down
19 changes: 19 additions & 0 deletions data/sslverify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2015 The Loadcat Authors. All rights reserved.

package data

type SSLVerify string

var SSLVerifys = []SSLVerify{
"off",
"on",
}

func (p SSLVerify) Label() string {
return SSLVerifyLabels[p]
}

var SSLVerifyLabels = map[SSLVerify]string{
"off": "OFF",
"on": "ON",
}
21 changes: 21 additions & 0 deletions docker-compose_base.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: '3.8'
services:
web:
build:
context: .
dockerfile: ./docker/base/Dockerfile
ports:
- "26590:26590"
- "80:80"
- "443:443"
volumes:
- loadcat:/var/lib/loadcat
- ./loadcatd:/usr/bin/loadcatd
- ./ui/templates:/var/lib/loadcat/ui/templates
working_dir: /var/lib/loadcat
command: loadcatd -config /etc/loadcat.conf
restart: unless-stopped

volumes:
loadcat:
driver: "local"
4 changes: 2 additions & 2 deletions docker/base/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM nginx:1.21.0
FROM nginx:stable

ADD docker/base/nginx.conf /etc/nginx/nginx.conf
ADD docker/base/loadcat.conf /etc/loadcat.conf
ADD loadcat /usr/bin/loadcat
ADD loadcatd /usr/bin/loadcat
ADD ui/templates /var/lib/loadcat/ui/templates

WORKDIR /var/lib/loadcat
Expand Down
2 changes: 1 addition & 1 deletion docker/dev/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM nginx:1.21.0
FROM nginx:stable

ADD nginx.conf /etc/nginx/nginx.conf
ADD loadcat.conf /etc/loadcat.conf
19 changes: 16 additions & 3 deletions feline/nginx/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,15 @@ server {
server_name {{.Balancer.Settings.Hostname}};

{{if eq .Balancer.Settings.Protocol "https"}}
ssl on;
ssl_certificate {{.Dir}}/server.crt;
ssl_certificate_key {{.Dir}}/server.key;

ssl_certificate /var/lib/loadcat/out/{{.Balancer.Id.Hex}}/server.crt;
ssl_certificate_key /var/lib/loadcat/out/{{.Balancer.Id.Hex}}/server.key;

{{end}}

{{if eq .Balancer.Settings.SSLOptions.SSLVerify "on"}}
ssl_client_certificate /var/lib/loadcat/out/{{.Balancer.Id.Hex}}/ca.crt;
ssl_verify_client on;
{{end}}

location / {
Expand Down Expand Up @@ -105,6 +111,13 @@ func (n *Nginx) Generate(dir string, bal *data.Balancer) error {
}
}

if bal.Settings.SSLOptions.SSLVerify == "on" {
err = ioutil.WriteFile(filepath.Join(dir, "ca.crt"), bal.Settings.SSLVerifyClient.ClientCertificate, 0666)
if err != nil {
return err
}
}

return nil
}

Expand Down
36 changes: 36 additions & 0 deletions ui/balancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ func ServeBalancerEditForm(w http.ResponseWriter, r *http.Request) {
err = TplBalancerEditForm.Execute(w, struct {
Balancer *data.Balancer
Protocols []data.Protocol
SSLVerifys []data.SSLVerify
Algorithms []data.Algorithm
CipherSuites []data.CipherSuite
}{
Balancer: bal,
Protocols: data.Protocols,
SSLVerifys: data.SSLVerifys,
Algorithms: data.Algorithms,
CipherSuites: data.CipherSuites,
})
Expand Down Expand Up @@ -144,7 +146,11 @@ func HandleBalancerUpdate(w http.ResponseWriter, r *http.Request) {
CipherSuite string `schema:"cipher_suite"`
Certificate *string `schema:"certificate"`
PrivateKey *string `schema:"private_key"`
SSLVerify string `schema:"sslverify"`
} `schema:"ssl_options"`
SSLVerifyClient struct {
ClientCertificate *string `schema:"clientcertificate"`
}
} `schema:"settings"`
}{}
err = schema.NewDecoder().Decode(&body, r.PostForm)
Expand All @@ -160,6 +166,13 @@ func HandleBalancerUpdate(w http.ResponseWriter, r *http.Request) {
bal.Settings.Algorithm = data.Algorithm(body.Settings.Algorithm)
if body.Settings.Protocol == "https" {
bal.Settings.SSLOptions.CipherSuite = "recommended"
bal.Settings.SSLOptions.SSLVerify = "off"
if body.Settings.SSLOptions.SSLVerify == "on" {
bal.Settings.SSLOptions.SSLVerify = data.SSLVerify(body.Settings.SSLOptions.SSLVerify)
if body.Settings.SSLVerifyClient.ClientCertificate != nil {
bal.Settings.SSLVerifyClient.ClientCertificate = []byte(*body.Settings.SSLVerifyClient.ClientCertificate)
}
}
if body.Settings.SSLOptions.Certificate != nil {
bal.Settings.SSLOptions.Certificate = []byte(*body.Settings.SSLOptions.Certificate)
}
Expand All @@ -180,6 +193,25 @@ func HandleBalancerUpdate(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/balancers/"+bal.Id.Hex(), http.StatusSeeOther)
}

func HandleBalancerDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
if !bson.IsObjectIdHex(vars["id"]) {
http.Error(w, "Not Found", http.StatusNotFound)
return
}
bal, err := data.GetBalancer(bson.ObjectIdHex(vars["id"]))
if err != nil {
panic(err)
}

err = bal.Delete()
if err != nil {
panic(err)
}

http.Redirect(w, r, "/balancers", http.StatusSeeOther)
}

func init() {
Router.NewRoute().
Methods("GET").
Expand All @@ -205,4 +237,8 @@ func init() {
Methods("POST").
Path("/balancers/{id}/edit").
Handler(http.HandlerFunc(HandleBalancerUpdate))
Router.NewRoute().
Methods("POST").
Path("/balancers/{id}/delete").
Handler(http.HandlerFunc(HandleBalancerDelete))
}
87 changes: 61 additions & 26 deletions ui/templates/balancerEditForm.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,33 @@
<textarea name="settings.ssl_options.private_key" class="form-control" id="inpPrivateKey" {{if .Balancer.Settings.SSLOptions.Fingerprint}}disabled{{end}}></textarea>
</div>
</div>

<div class="form-group">
<label for="inpSSLVerify" class="col-sm-2 control-label">SSL Verify Client</label>
<div class="col-sm-4">
<select name="settings.ssl_options.sslverify" class="form-control" id="inpSSLVerify">
{{range $sslverify := .SSLVerifys}}
<option value="{{$sslverify}}" {{if eq $sslverify $.Balancer.Settings.SSLOptions.SSLVerify}}selected{{end}}>{{$sslverify.Label}}</option>
{{end}}
</select>
</div>
</div>
<div id="clientCertificateGroup" class="form-group">
<label for="inpClientCertificate" class="col-sm-2 control-label">Client Certificate</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="inpClientCertificate" value="{{printf "% x" .Balancer.Settings.SSLVerifyClient.ClientCertificate}}" readonly>
<textarea name="settings.sslverifyclient.clientcertificate" class="form-control" id="inpClientCertificate"></textarea>
</div>
</div>
<br>
</fieldset>

<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Update</button>
<a href="/balancers/{{.Balancer.Id.Hex}}" class="btn btn-default">Cancel</a>
<button type="button" class="btn btn-danger" onclick="deleteBalancer()">Delete</button>
</div>
</div>
</div>
</form>
</div>
</div>
Expand All @@ -109,31 +126,49 @@
{{define "sidebar"}}

{{end}}

{{define "scripts"}}
<script type="text/javascript">
$('#inpProtocol').on('change', function() {
switch($(this).val()) {
case 'http':
$('#setSSLOptions').addClass('hidden')
break
case 'https':
$('#setSSLOptions').removeClass('hidden')
break
}
})
.trigger('change')
<script type="text/javascript">
$('#inpProtocol').on('change', function() {
switch($(this).val()) {
case 'http':
$('#setSSLOptions').addClass('hidden');
break;
case 'https':
$('#setSSLOptions').removeClass('hidden');
break;
}
}).trigger('change');

$('#btnReplaceCertificate').on('click', function(event) {
event.preventDefault()
$('#inpCertificate').removeClass('hidden').removeAttr('disabled')
$('#inpPrivateKey').removeAttr('disabled').closest('.form-group').removeClass('hidden')
$('#inpFingerprint').detach()
$('#btnReplaceCertificate').closest('div').detach()
})
$('#inpSSLVerify').on('change', function() {
if ($(this).val() === 'on') {
$('#clientCertificateGroup').removeClass('hidden');
} else {
$('#clientCertificateGroup').addClass('hidden');
}
}).trigger('change');

{{if .Balancer.Settings.SSLOptions.Fingerprint}}
$('#inpDNSNames ')
{{end}}
</script>
$('#btnReplaceCertificate').on('click', function(event) {
event.preventDefault();
$('#inpCertificate').removeClass('hidden').removeAttr('disabled');
$('#inpPrivateKey').removeAttr('disabled').closest('.form-group').removeClass('hidden');
$('#inpFingerprint').detach();
$('#btnReplaceCertificate').closest('div').detach();
});


function deleteBalancer() {
if (confirm("Are you sure you want to delete this balancer?")) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/balancers/{{.Balancer.Id.Hex}}/delete", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
window.location.href = "/balancers";
}
};
xhr.send();
}
}
</script>
{{end}}