diff --git a/docker/docker.go b/docker/docker.go index f90c76a..d4d6f5e 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -50,6 +50,12 @@ func Run(Config cfg.Config){ panic("Node is not a swarm manager."); } + sshKeypair := generateKeypair(); + ApiClient.ConfigCreate(context.Background(), swarm.ConfigSpec{ + Data: sshKeypair.public, + Annotations: swarm.Annotations{Name: "blazenaSSHPublicKey"}, + }); + server := &http.Server{ Addr: ":1234", } @@ -88,13 +94,15 @@ func Run(Config cfg.Config){ }(); - fmt.Println("Api started!"); + fmt.Println("Api has been started!"); time.Sleep(10*time.Millisecond); <-ctx.Done(); fmt.Println("Stopping http server."); server.Close(); + ApiClient.NetworkRemove(context.Background(), "blazenaPohar"); + ApiClient.ConfigRemove(context.Background(), "blazenaSSHPublicKey") fmt.Println("Exiting!"); } @@ -126,13 +134,12 @@ func listServices(w http.ResponseWriter, r *http.Request){ var services []aService; - for _, service:= range list{ + for _, service := range list{ var settings map[string]string = service.Spec.Labels; if(settings["blazena.enable"] != "true"){ continue; - } targetVolumes := strings.Split(settings["blazena.volumes"], ","); diff --git a/docker/prepare.go b/docker/prepare.go index 6a2c6bb..1447b3c 100644 --- a/docker/prepare.go +++ b/docker/prepare.go @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/image" + "github.com/docker/docker/client" ) func prepare(w http.ResponseWriter, r *http.Request){ @@ -44,6 +45,45 @@ func prepare(w http.ResponseWriter, r *http.Request){ panic("Failed to inspect service."+ err.Error()); } + + labels := inspectResoults.Spec.Labels; + + pullBlazenaImage(); + createHelper(labels["blazena.node"], bodyDecoded.VolumeId); + + time.Sleep(7*time.Second); + + fmt.Fprint(w, bodyDecoded.ServiceId); +} + +func contains(slice []string, str string) bool { + for _, s := range slice { + if s == str { + return true + } + } + return false +} + +//By gpt (I'm lazy) +func getConfigIDByName(cli *client.Client, name string) (string, error) { + ctx := context.Background() + + configs, err := cli.ConfigList(ctx, swarm.ConfigListOptions{}) + if err != nil { + return "", err + } + + for _, cfg := range configs { + if cfg.Spec.Name == name { + return cfg.ID, nil + } + } + + return "", fmt.Errorf("config not found: %s", name) +} + +func pullBlazenaImage(){ authConfig := registry.AuthConfig{ Username: theConfig.RegistryAuth.Username, Password: theConfig.RegistryAuth.Password, @@ -63,19 +103,20 @@ func prepare(w http.ResponseWriter, r *http.Request){ defer ipc.Close(); io.Copy(io.Discard, ipc); +} - - labels := inspectResoults.Spec.Labels; - +func createHelper(targetNode string, targetVolume string){ maxConcurrent := uint64(1); totalCompletions := uint64(1); stopGracePeriod := time.Second * 5; - targetNode := labels["blazena.node"]; helperCommand := `ssh-keygen -t ed25519 -f /host_key && \ - echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIByYbl8vu946LPycSO5pBohq3vMvvl+wX7snu1Bqpd7p test" > /root/.ssh/authorized_keys && \ /usr/sbin/sshd -h /host_key -p 2222 -D`; - + sshKeyConfigId, err := getConfigIDByName(ApiClient, "blazenaSSHPublicKey"); + + if err != nil { + panic("Docker needs both id and name to mount config for some reason and getting id of it failed!"+err.Error()); + } _, err = ApiClient.ServiceCreate(context.Background(), swarm.ServiceSpec{ Annotations: swarm.Annotations{ Name: "BlazenaHelper", @@ -93,13 +134,25 @@ func prepare(w http.ResponseWriter, r *http.Request){ Command: []string{"sh", "-c", helperCommand}, Mounts: []mount.Mount{ mount.Mount{ - Source: bodyDecoded.VolumeId, + Source: targetVolume, Target: "/volume", Type: "volume", ReadOnly: true, }, }, StopGracePeriod: &stopGracePeriod, + Configs: []*swarm.ConfigReference{ + &swarm.ConfigReference{ + ConfigID: sshKeyConfigId, + ConfigName: "blazenaSSHPublicKey", + File: &swarm.ConfigReferenceFileTarget{ + Name: "/root/.ssh/authorized_keys", + Mode: 0600, + UID: "0", + GID: "0", + }, + }, + }, }, Placement: &swarm.Placement{ Constraints: []string{"node.hostname=="+targetNode}, @@ -114,17 +167,4 @@ func prepare(w http.ResponseWriter, r *http.Request){ panic("Failed to create helper service."+ err.Error()); } - time.Sleep(7*time.Second); - - fmt.Fprint(w, bodyDecoded.ServiceId); } - -func contains(slice []string, str string) bool { - for _, s := range slice { - if s == str { - return true - } - } - return false -} - diff --git a/docker/ssh.go b/docker/ssh.go new file mode 100644 index 0000000..4336fb6 --- /dev/null +++ b/docker/ssh.go @@ -0,0 +1,45 @@ +package docker + +import ( + "crypto/ed25519" + "crypto/rand" + "crypto/x509" + "encoding/pem" + + "golang.org/x/crypto/ssh" +) + +type Keypair struct { + public []byte + private []byte +} + +func generateKeypair() Keypair { + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + panic(err) + } + + privBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + panic(err) + } + + privPem := pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: privBytes, + }) + + sshPubKey, err := ssh.NewPublicKey(publicKey) + if err != nil { + panic(err) + } + + pubBytes := ssh.MarshalAuthorizedKey(sshPubKey) + + return Keypair{ + private: privPem, + public: pubBytes, + }; + +} diff --git a/go.mod b/go.mod index 57b31d5..c642ff1 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,8 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect go.opentelemetry.io/otel/metric v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect - golang.org/x/sys v0.40.0 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/sys v0.42.0 // indirect golang.org/x/time v0.14.0 // indirect gotest.tools/v3 v3.5.2 // indirect ) diff --git a/go.sum b/go.sum index 069b9b3..c9bae05 100644 --- a/go.sum +++ b/go.sum @@ -77,12 +77,18 @@ go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZY go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=