{"slug": "building-enterprise-active-directory-ldap-dynamic-rbac-in-go-flutter-with-google", "title": "Building Enterprise Active Directory, LDAP & Dynamic RBAC in Go & Flutter with Google Antigravity", "summary": "Gubernator v2.20.0 introduces an enterprise security engine with Active Directory and LDAP integration, dynamic RBAC, and a Flutter Web UI, developed using Google Antigravity as an AI pair programmer. The feature supports multi-server directory dialers, LDAPS/StartTLS, and HMAC-SHA256 JWT tokens, with three operational tiers: admin, operator, and readonly.", "body_md": "When building a lightweight container orchestrator like ** Gubernator (gbnt)** — designed to strike the perfect balance between the\n\nWhile a default `admin`\n\ncredential works well for local dev environments, moving into enterprise production with multi-disciplinary engineering teams demands:\n\n`memberOf`\n\n) to orchestrator roles.In this article, we explore the complete architecture of the enterprise security engine introduced in **Gubernator v2.20.0**, and how we leveraged **Google Antigravity (AGY)** as an autonomous AI pair programmer to design, implement, test, and verify this Full-Stack feature (Go + Flutter Web) across a live 3-node cluster.\n\nWe designed a decoupled, asymmetric architecture connecting identity providers, REST API middleware, and the Flutter Web UI:\n\n```\n ┌────────────────────────────────────────────────────────┐\n │                   GUBERNATOR WEB UI                    │\n │   - Modern Login Screen with Domain / AD Selector      │\n │   - Header Role Badge: Admin |  Ops |  Read-Only.      │\n └──────────────────────────┬─────────────────────────────┘\n                            │ (REST /api/auth/login)\n                            ▼\n ┌────────────────────────────────────────────────────────┐\n │             GUBERNATOR CORE AUTH ENGINE (Go)           │\n │  - Local Emergency Admin (admin / admin fallback)      │\n │  - Multi-Server Active Directory / OpenLDAP Dialers    │\n │  - LDAPS (Port 636) & StartTLS (Port 389) Handshake    │\n │  - Dynamic Group DN -> RBAC Role Resolution            │\n │  - Cryptographic HMAC-SHA256 JWT Token Signing         │\n └─────────────┬────────────────────────────┬─────────────┘\n               │                            │\n               ▼                            ▼\n ┌───────────────────────────┐ ┌──────────────────────────┐\n │  Primary Active Directory │ │ Secondary LDAP Server    │\n │   dc1.corporate.local     │ │   dc2.dr-site.local      │\n └───────────────────────────┘ └──────────────────────────┘\n```\n\nWe established three distinct operational tiers:\n\n| Operational Capability | `admin` |\n`operator` |\n`readonly` |\n|---|---|---|---|\nOverview, Metrics & SRE Telemetry |\n✅ Full | ✅ Full | ✅ Full |\nDeploy Stacks (`docker-compose.yml` ) |\n✅ Full | ✅ Full | ❌ Restricted |\nRedeploy & Duplicate Stacks |\n✅ Full | ✅ Full | ❌ Restricted |\nDelete Stacks |\n✅ Full | ❌ Restricted | ❌ Restricted |\nTask Lifecycle (Start / Stop / Restart) |\n✅ Full | ✅ Full | ❌ Restricted |\nContainer & Node Terminal Shell |\n✅ Full | ✅ Full | ❌ Restricted |\nNode Fleet Management (Drain / Activate / Leave) |\n✅ Full | ❌ Restricted | ❌ Restricted |\nCaddy TLS Certificates & Ingress Routes |\n✅ Full | ❌ Restricted | ❌ Restricted |\nActive Directory & LDAP Directory Settings |\n✅ Full | ❌ Restricted | ❌ Restricted |\nGrafana, Jaeger & Weave Scope Dashboards |\n✅ Full | ✅ Full | ✅ Full |\n\n`internal/auth/`\n\n)\nFor LDAP/Active Directory interactions, we used `github.com/go-ldap/ldap/v3`\n\n, and for session management `github.com/golang-jwt/jwt/v5`\n\n.\n\nAuthentication follows a secure two-phase pattern:\n\n`BindDN`\n\n/ `BindPassword`\n\n) to query the directory.`(&(objectClass=user)(sAMAccountName=%s))`\n\n).\n\n```\nfunc AuthenticateLDAP(cfg db.LDAPConfig, username, password string) (*AuthResult, error) {\n    conn, err := ConnectLDAP(cfg)\n    if err != nil {\n        return nil, err\n    }\n    defer conn.Close()\n\n    // 1. Initial service account bind\n    if cfg.BindDN != \"\" && cfg.BindPassword != \"\" {\n        if err := conn.Bind(cfg.BindDN, cfg.BindPassword); err != nil {\n            return nil, fmt.Errorf(\"service account bind failed: %w\", err)\n        }\n    }\n\n    // 2. Search for the user\n    filter := fmt.Sprintf(cfg.UserFilter, ldap.EscapeFilter(username))\n    searchReq := ldap.NewSearchRequest(\n        cfg.BaseDN,\n        ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,\n        filter,\n        []string{\"dn\", \"displayName\", \"mail\", \"memberOf\"},\n        nil,\n    )\n    sr, err := conn.Search(searchReq)\n    if err != nil || len(sr.Entries) == 0 {\n        return nil, errors.New(\"user not found in directory\")\n    }\n\n    userEntry := sr.Entries[0]\n\n    // 3. Direct user bind to verify password\n    userConn, err := ConnectLDAP(cfg)\n    if err != nil {\n        return nil, err\n    }\n    defer userConn.Close()\n\n    if err := userConn.Bind(userEntry.DN, password); err != nil {\n        return nil, errors.New(\"invalid credentials\")\n    }\n\n    // 4. Map groups to RBAC role\n    groups := userEntry.GetAttributeValues(\"memberOf\")\n    role := ResolveRole(cfg, groups)\n\n    return &AuthResult{\n        UserDN:      userEntry.DN,\n        Username:    username,\n        DisplayName: userEntry.GetAttributeValue(\"displayName\"),\n        Email:       userEntry.GetAttributeValue(\"mail\"),\n        Groups:      groups,\n        Role:        role,\n    }, nil\n}\n```\n\nGubernator inspects the user's `memberOf`\n\ngroup list and matches them against the configured group DNs:\n\n```\nfunc ResolveRole(cfg db.LDAPConfig, userGroups []string) Role {\n    matchesGroup := func(targetGroup string) bool {\n        if targetGroup == \"\" { return false }\n        target := strings.ToLower(strings.TrimSpace(targetGroup))\n        for _, g := range userGroups {\n            if strings.ToLower(strings.TrimSpace(g)) == target {\n                return true\n            }\n        }\n        return false\n    }\n\n    if matchesGroup(cfg.AdminGroupDN) { return RoleAdmin }\n    if matchesGroup(cfg.OperatorGroupDN) { return RoleOperator }\n    if matchesGroup(cfg.ReadOnlyGroupDN) { return RoleReadOnly }\n\n    return NormalizeRole(cfg.DefaultRole)\n}\n```\n\nGubernator's Web Dashboard is built with **Flutter Web** and **Material Design 3**, compiled and embedded directly into the Go binary (`go:embed`\n\n).\n\nOperators can select their target authentication provider (`Corporate Active Directory`\n\n, `DR Site LDAP`\n\n, or `Local Administrator`\n\n):\n\nIn the new **Seguridad & AD** tab, cluster administrators can configure directory servers, TLS certificates, and run a live **\"Test Connection\"** diagnostic tool:\n\nThe dashboard header displays the active user and their assigned role (`ADMIN`\n\n, `⚡ OPERATOR`\n\n, `READ-ONLY`\n\n). Mutating actions (e.g., Delete Stack, Drain Node, Shell) are automatically disabled for read-only audit accounts.\n\nWe utilized **Google Antigravity (AGY)** as an autonomous AI pair programmer to build this feature end-to-end. AGY accelerated the development cycle through several key workflows:\n\n**Architectural Planning**:\n\nBefore writing code, Antigravity produced a comprehensive implementation plan (`implementation_plan.md`\n\n) outlining the GORM schema changes (`LDAPConfig`\n\n), RBAC authorization matrix, and API routes.\n\n**Synchronized Full-Stack Implementation**:\n\nIn a single coordinated session, Antigravity:\n\n`internal/auth/`\n\nengine with LDAP dialers, JWT session handlers, and Gin middlewares.`login_screen.dart`\n\n, `security_page.dart`\n\n, and state models).`legions_page.dart`\n\n, `tasks_page.dart`\n\n, `centurions_page.dart`\n\n) with RBAC permission guards.**Live Cluster Testing & Verification**:\n\nUsing automated commands across a 3-node multipass cluster (`gbnt-manager`\n\n, `gbnt-worker1`\n\n, `gbnt-worker2`\n\n), Antigravity:\n\n`curl`\n\n(valid login, invalid login, LDAP connection tests, configuration lifecycle).`go test ./internal/auth/...`\n\n) with 100% pass rates.**Automated Documentation & Release**:\n\n`docs/auth-rbac.md`\n\n`v2.20.0`\n\n, created git release tags, and triggered GitHub Pages publishing.Adding Active Directory SSO and RBAC allows teams to deploy Gubernator in enterprise production environments that require enterprise security compliance without the operational overhead of Kubernetes.\n\nCheck out Gubernator and try it out:\n\nWhat do you think about this hybrid approach to container orchestration? Let us know your thoughts and suggestions in the comments!", "url": "https://wpnews.pro/news/building-enterprise-active-directory-ldap-dynamic-rbac-in-go-flutter-with-google", "canonical_source": "https://dev.to/gde/building-enterprise-active-directory-ldap-dynamic-rbac-in-go-flutter-with-google-antigravity-4al4", "published_at": "2026-08-17 09:00:16+00:00", "updated_at": "2026-08-17 09:13:03.035152+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Gubernator", "Google Antigravity", "Go", "Flutter", "Active Directory", "OpenLDAP", "JWT", "HMAC-SHA256"], "alternates": {"html": "https://wpnews.pro/news/building-enterprise-active-directory-ldap-dynamic-rbac-in-go-flutter-with-google", "markdown": "https://wpnews.pro/news/building-enterprise-active-directory-ldap-dynamic-rbac-in-go-flutter-with-google.md", "text": "https://wpnews.pro/news/building-enterprise-active-directory-ldap-dynamic-rbac-in-go-flutter-with-google.txt", "jsonld": "https://wpnews.pro/news/building-enterprise-active-directory-ldap-dynamic-rbac-in-go-flutter-with-google.jsonld"}}