Last active
February 11, 2025 23:56
-
-
Save vdparikh/d0553586b0651bf293835522dc61a89c to your computer and use it in GitHub Desktop.
Revisions
-
vdparikh revised this gist
Feb 11, 2025 . 2 changed files with 233 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,233 @@ package main import ( "crypto/rand" "encoding/base64" "encoding/json" "fmt" "log" "net/http" "os" "strings" "github.com/go-ldap/ldap/v3" "github.com/slack-go/slack" ) // GenerateRandomPassword generates a random password of a given length, avoiding specified characters. func GenerateRandomPassword(length int, avoidChars string) (string, error) { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?`~" var validChars []rune // Filter out characters to avoid for _, char := range charset { if !strings.ContainsRune(avoidChars, char) { validChars = append(validChars, char) } } if len(validChars) == 0 { return "", fmt.Errorf("no valid characters available after filtering") } bytes := make([]byte, length) _, err := rand.Read(bytes) if err != nil { return "", err } password := make([]rune, length) for i := range password { password[i] = validChars[int(bytes[i])%len(validChars)] } return string(password), nil } // CheckIfUserIsOwner checks if the Slack user is the owner of the service account in Active Directory. func CheckIfUserIsOwner(serviceAccount, userEmail string) (bool, error) { // Connect to LDAP server l, err := ldap.Dial("tcp", "ldap.example.com:389") if err != nil { return false, err } defer l.Close() // Bind with admin credentials err = l.Bind("[email protected]", "adminpassword") if err != nil { return false, err } // Search for the service account's managedBy attribute searchRequest := ldap.NewSearchRequest( fmt.Sprintf("CN=%s,OU=ServiceAccounts,DC=example,DC=com", serviceAccount), ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{"managedBy"}, nil, ) result, err := l.Search(searchRequest) if err != nil { return false, err } if len(result.Entries) == 0 { return false, fmt.Errorf("service account not found") } // Get the managedBy attribute (owner of the service account) managedBy := result.Entries[0].GetAttributeValue("managedBy") if managedBy == "" { return false, fmt.Errorf("managedBy attribute not found for service account") } // Compare the managedBy value with the Slack user's email return managedBy == userEmail, nil } // UpdateADPassword updates the password in Active Directory. func UpdateADPassword(serviceAccount, newPassword string) error { // Connect to LDAP server l, err := ldap.Dial("tcp", "ldap.example.com:389") if err != nil { return err } defer l.Close() // Bind with admin credentials err = l.Bind("[email protected]", "adminpassword") if err != nil { return err } // Prepare the password update request modifyRequest := ldap.NewModifyRequest(fmt.Sprintf("CN=%s,OU=ServiceAccounts,DC=example,DC=com", serviceAccount), nil) modifyRequest.Replace("unicodePwd", []byte(fmt.Sprintf("\"%s\"", newPassword))) // Update the password err = l.Modify(modifyRequest) if err != nil { return err } log.Printf("Password updated in Active Directory for service account: %s\n", serviceAccount) return nil } // RotatePasswordInE rotates the password in E. func RotatePasswordInE(serviceAccount, newPassword string) error { // Replace with actual E API calls // Example: Call E API to update the password log.Printf("Password rotated in E for service account: %s\n", serviceAccount) return nil } // RotatePasswordInVault rotates the password in HashiCorp Vault. func RotatePasswordInVault(serviceAccount, newPassword string) error { // Initialize Vault client config := vault.DefaultConfig() config.Address = "http://vault.example.com:8200" client, err := vault.NewClient(config) if err != nil { return err } // Set the Vault token (replace with your actual token) client.SetToken("s.xxxxxxxx") // Write the new password to Vault secretData := map[string]interface{}{ "password": newPassword, } _, err = client.Logical().Write(fmt.Sprintf("secret/data/%s", serviceAccount), secretData) if err != nil { return err } log.Printf("Password rotated in HashiCorp Vault for service account: %s\n", serviceAccount) return nil } // SlackHandler handles Slack slash commands. func SlackHandler(w http.ResponseWriter, r *http.Request) { // Parse the Slack slash command payload err := r.ParseForm() if err != nil { http.Error(w, "Failed to parse form data", http.StatusBadRequest) return } // Extract the Slack user ID and service account name slackUserID := r.FormValue("user_id") serviceAccount := r.FormValue("text") // Initialize Slack client slackToken := os.Getenv("SLACK_TOKEN") slackClient := slack.New(slackToken) // Get the Slack user's email userInfo, err := slackClient.GetUserInfo(slackUserID) if err != nil { http.Error(w, "Failed to get Slack user info", http.StatusInternalServerError) return } userEmail := userInfo.Profile.Email // Check if the Slack user is the owner of the service account isOwner, err := CheckIfUserIsOwner(serviceAccount, userEmail) if err != nil { http.Error(w, fmt.Sprintf("Failed to validate ownership: %v", err), http.StatusInternalServerError) return } if !isOwner { http.Error(w, "You are not the owner of this service account", http.StatusForbidden) return } // Define characters to avoid (e.g., quotes, special characters) avoidChars := `"'` // Generate a random password, avoiding specified characters newPassword, err := GenerateRandomPassword(16, avoidChars) if err != nil { http.Error(w, fmt.Sprintf("Failed to generate random password: %v", err), http.StatusInternalServerError) return } // Update password in Active Directory err = UpdateADPassword(serviceAccount, newPassword) if err != nil { http.Error(w, fmt.Sprintf("Failed to update password in Active Directory: %v", err), http.StatusInternalServerError) return } // Rotate password in E err = RotatePasswordInE(serviceAccount, newPassword) if err != nil { http.Error(w, fmt.Sprintf("Failed to rotate password in E: %v", err), http.StatusInternalServerError) return } // Rotate password in HashiCorp Vault err = RotatePasswordInVault(serviceAccount, newPassword) if err != nil { http.Error(w, fmt.Sprintf("Failed to rotate password in HashiCorp Vault: %v", err), http.StatusInternalServerError) return } // Respond to Slack w.WriteHeader(http.StatusOK) w.Write([]byte("Password rotation completed successfully!")) } func main() { // Start the web server to handle Slack requests http.HandleFunc("/rotate-password", SlackHandler) log.Println("Server started on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } -
vdparikh created this gist
Feb 11, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,195 @@ package main import ( "crypto/rand" "encoding/base64" "fmt" "log" "os" "strings" "github.com/go-ldap/ldap/v3" vault "github.com/hashicorp/vault/api" ) // GenerateRandomPassword generates a random password of a given length, avoiding specified characters. func GenerateRandomPassword(length int, avoidChars string) (string, error) { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?`~" var validChars []rune // Filter out characters to avoid for _, char := range charset { if !strings.ContainsRune(avoidChars, char) { validChars = append(validChars, char) } } if len(validChars) == 0 { return "", fmt.Errorf("no valid characters available after filtering") } bytes := make([]byte, length) _, err := rand.Read(bytes) if err != nil { return "", err } password := make([]rune, length) for i := range password { password[i] = validChars[int(bytes[i])%len(validChars)] } return string(password), nil } // CheckIfUserIsOwner checks if the invoking user is the owner of the service account in Active Directory. func CheckIfUserIsOwner(serviceAccount, invokingUser string) (bool, error) { // Connect to LDAP server l, err := ldap.Dial("tcp", "ldap.example.com:389") if err != nil { return false, err } defer l.Close() // Bind with admin credentials err = l.Bind("[email protected]", "adminpassword") if err != nil { return false, err } // Search for the service account's managedBy attribute searchRequest := ldap.NewSearchRequest( fmt.Sprintf("CN=%s,OU=ServiceAccounts,DC=example,DC=com", serviceAccount), ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", []string{"managedBy"}, nil, ) result, err := l.Search(searchRequest) if err != nil { return false, err } if len(result.Entries) == 0 { return false, fmt.Errorf("service account not found") } // Get the managedBy attribute (owner of the service account) managedBy := result.Entries[0].GetAttributeValue("managedBy") if managedBy == "" { return false, fmt.Errorf("managedBy attribute not found for service account") } // Compare the managedBy value with the invoking user return managedBy == invokingUser, nil } // UpdateADPassword updates the password in Active Directory. func UpdateADPassword(serviceAccount, newPassword string) error { // Connect to LDAP server l, err := ldap.Dial("tcp", "ldap.example.com:389") if err != nil { return err } defer l.Close() // Bind with admin credentials err = l.Bind("[email protected]", "adminpassword") if err != nil { return err } // Prepare the password update request modifyRequest := ldap.NewModifyRequest(fmt.Sprintf("CN=%s,OU=ServiceAccounts,DC=example,DC=com", serviceAccount), nil) modifyRequest.Replace("unicodePwd", []byte(fmt.Sprintf("\"%s\"", newPassword))) // Update the password err = l.Modify(modifyRequest) if err != nil { return err } log.Printf("Password updated in Active Directory for service account: %s\n", serviceAccount) return nil } // RotatePasswordInE rotates the password in E. func RotatePasswordInE(serviceAccount, newPassword string) error { // Replace with actual E API calls // Example: Call E API to update the password log.Printf("Password rotated in E for service account: %s\n", serviceAccount) return nil } // RotatePasswordInVault rotates the password in HashiCorp Vault. func RotatePasswordInVault(serviceAccount, newPassword string) error { // Initialize Vault client config := vault.DefaultConfig() config.Address = "http://vault.example.com:8200" client, err := vault.NewClient(config) if err != nil { return err } // Set the Vault token (replace with your actual token) client.SetToken("s.xxxxxxxx") // Write the new password to Vault secretData := map[string]interface{}{ "password": newPassword, } _, err = client.Logical().Write(fmt.Sprintf("secret/data/%s", serviceAccount), secretData) if err != nil { return err } log.Printf("Password rotated in HashiCorp Vault for service account: %s\n", serviceAccount) return nil } func main() { serviceAccount := "svc-account" invokingUser := os.Getenv("USER") // Replace with actual invoking user (e.g., from environment or input) // Check if the invoking user is the owner of the service account isOwner, err := CheckIfUserIsOwner(serviceAccount, invokingUser) if err != nil { log.Fatalf("Failed to check service account ownership: %v", err) } if !isOwner { log.Fatalf("User %s is not the owner of service account %s", invokingUser, serviceAccount) } // Define characters to avoid (e.g., quotes, special characters) avoidChars := `"'` // Generate a random password, avoiding specified characters newPassword, err := GenerateRandomPassword(16, avoidChars) if err != nil { log.Fatalf("Failed to generate random password: %v", err) } // Update password in Active Directory err = UpdateADPassword(serviceAccount, newPassword) if err != nil { log.Fatalf("Failed to update password in Active Directory: %v", err) } // Rotate password in E err = RotatePasswordInE(serviceAccount, newPassword) if err != nil { log.Fatalf("Failed to rotate password in E: %v", err) } // Rotate password in HashiCorp Vault err = RotatePasswordInVault(serviceAccount, newPassword) if err != nil { log.Fatalf("Failed to rotate password in HashiCorp Vault: %v", err) } log.Println("Password rotation completed successfully!") }