From d5cc14ce49e0630c089977f06ce492315b8d1e30 Mon Sep 17 00:00:00 2001 From: Vishal Dalwadi <51291657+VishalDalwadi@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:05:58 +0530 Subject: [PATCH 1/4] Patch: Okta IDP Integration (#3586) * feat(go): add support for okta. * feat(go): update docs link. * feat(go): handle okta. * feat(go): handle okta. --- go.mod | 14 +++++ go.sum | 83 ++++++++++++++++++++++++++++ logic/settings.go | 4 +- models/settings.go | 2 + pro/auth/auth.go | 5 +- pro/auth/error.go | 2 +- pro/auth/okta.go | 9 +++ pro/auth/sync.go | 6 ++ pro/auth/templates.go | 4 +- pro/idp/okta/okta.go | 124 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 247 insertions(+), 6 deletions(-) create mode 100644 pro/auth/okta.go create mode 100644 pro/idp/okta/okta.go diff --git a/go.mod b/go.mod index 585ea61f..b48aed50 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e github.com/guumaster/tablewriter v0.0.10 github.com/matryer/is v1.4.1 + github.com/okta/okta-sdk-golang/v5 v5.0.6 github.com/pquerna/otp v1.5.0 github.com/spf13/cobra v1.9.1 google.golang.org/api v0.238.0 @@ -61,11 +62,15 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.7.0 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.2 // indirect @@ -77,6 +82,15 @@ require ( github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/jwx v1.2.29 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect + github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/seancfoley/bintree v1.3.1 // indirect diff --git a/go.sum b/go.sum index cc71ce73..0956812a 100644 --- a/go.sum +++ b/go.sum @@ -12,18 +12,25 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8 github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c-robinson/iplib v1.0.8 h1:exDRViDyL9UBLcfmlxxkY5odWX5092nPsQIykHXhIn4= github.com/c-robinson/iplib v1.0.8/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o= github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -42,6 +49,8 @@ github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAu github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= @@ -50,6 +59,7 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= @@ -84,16 +94,33 @@ github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= +github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx v1.2.29 h1:QT0utmUJ4/12rmsVQrJ3u55bycPkKqGYuGT4tyRhxSQ= +github.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= @@ -105,6 +132,12 @@ github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEu github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= +github.com/okta/okta-sdk-golang/v5 v5.0.6 h1:p7ptDMB1KxQ/7xSh+6FhMSybwl+ubTV4f1oL4N0Bu6U= +github.com/okta/okta-sdk-golang/v5 v5.0.6/go.mod h1:T/vmECtJX33YPZSVD+sorebd8LLhe38Bi/VrFTjgVX0= +github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 h1:pSCLCl6joCFRnjpeojzOpEYs4q7Vditq8fySFG5ap3Y= +github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posthog/posthog-go v1.5.12 h1:nxK/z5QLCFxwzxV8GNvVd4Y1wJ++zJSWMGEtzU+/HLM= @@ -132,12 +165,21 @@ github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wx github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/txn2/txeh v1.5.5 h1:UN4e/lCK5HGw/gGAi2GCVrNKg0GTCUWs7gs5riaZlz4= github.com/txn2/txeh v1.5.5/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= @@ -156,22 +198,63 @@ go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKr go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ= google.golang.org/api v0.238.0 h1:+EldkglWIg/pWjkq97sd+XxH7PxakNYoe/rkSTbnvOs= diff --git a/logic/settings.go b/logic/settings.go index b889b6e0..5217c2e9 100644 --- a/logic/settings.go +++ b/logic/settings.go @@ -245,7 +245,7 @@ func GetAuthProviderInfo(settings models.ServerSettings) (pi []string) { var authProvider = "" defer func() { - if authProvider == "oidc" { + if authProvider == "okta" || authProvider == "oidc" { if settings.OIDCIssuer != "" { pi = append(pi, settings.OIDCIssuer) } else { @@ -256,7 +256,7 @@ func GetAuthProviderInfo(settings models.ServerSettings) (pi []string) { if settings.AuthProvider != "" && settings.ClientID != "" && settings.ClientSecret != "" { authProvider = strings.ToLower(settings.AuthProvider) - if authProvider == "google" || authProvider == "azure-ad" || authProvider == "github" || authProvider == "oidc" { + if authProvider == "google" || authProvider == "azure-ad" || authProvider == "github" || authProvider == "oidc" || authProvider == "okta" { return []string{authProvider, settings.ClientID, settings.ClientSecret} } else { authProvider = "" diff --git a/models/settings.go b/models/settings.go index 8e5f7781..da73de54 100644 --- a/models/settings.go +++ b/models/settings.go @@ -19,6 +19,8 @@ type ServerSettings struct { GoogleAdminEmail string `json:"google_admin_email"` GoogleSACredsJson string `json:"google_sa_creds_json"` AzureTenant string `json:"azure_tenant"` + OktaOrgURL string `json:"okta_org_url"` + OktaAPIToken string `json:"okta_api_token"` UserFilters []string `json:"user_filters"` GroupFilters []string `json:"group_filters"` IDPSyncInterval string `json:"idp_sync_interval"` diff --git a/pro/auth/auth.go b/pro/auth/auth.go index 33c271ff..7e103baf 100644 --- a/pro/auth/auth.go +++ b/pro/auth/auth.go @@ -27,6 +27,7 @@ const ( google_provider_name = "google" azure_ad_provider_name = "azure-ad" github_provider_name = "github" + okta_provider_name = "okta" oidc_provider_name = "oidc" verify_user = "verifyuser" user_signin_length = 16 @@ -85,6 +86,8 @@ func getCurrentAuthFunctions() map[string]interface{} { return azure_ad_functions case github_provider_name: return github_functions + case okta_provider_name: + return okta_functions case oidc_provider_name: return oidc_functions default: @@ -124,7 +127,7 @@ func InitializeAuthProvider() string { logger.Log(1, "external OAuth detected, proceeding with https redirect: ("+serverConn+")") } - if authInfo[0] == "oidc" { + if authInfo[0] == "okta" || authInfo[0] == "oidc" { functions[init_provider].(func(string, string, string, string))(serverConn+"/api/oauth/callback", authInfo[1], authInfo[2], authInfo[3]) return authInfo[0] } diff --git a/pro/auth/error.go b/pro/auth/error.go index eac61b27..0ecd276f 100644 --- a/pro/auth/error.go +++ b/pro/auth/error.go @@ -93,7 +93,7 @@ var htmlBaseTemplate = ` ` var oauthNotConfigured = fmt.Sprintf(htmlBaseTemplate, `

Your Netmaker server does not have OAuth configured.

-

Please visit the docs here to learn how to.

`) +

Please visit the docs here to learn how to.

`) var oauthStateInvalid = fmt.Sprintf(htmlBaseTemplate, `

Invalid OAuth Session. Please re-try again.

`) diff --git a/pro/auth/okta.go b/pro/auth/okta.go new file mode 100644 index 00000000..513b747e --- /dev/null +++ b/pro/auth/okta.go @@ -0,0 +1,9 @@ +package auth + +var okta_functions = map[string]interface{}{ + init_provider: initOIDC, + get_user_info: getOIDCUserInfo, + handle_callback: handleOIDCCallback, + handle_login: handleOIDCLogin, + verify_user: verifyOIDCUser, +} diff --git a/pro/auth/sync.go b/pro/auth/sync.go index 3c4e44a9..e98921ae 100644 --- a/pro/auth/sync.go +++ b/pro/auth/sync.go @@ -10,6 +10,7 @@ import ( "github.com/gravitl/netmaker/pro/idp" "github.com/gravitl/netmaker/pro/idp/azure" "github.com/gravitl/netmaker/pro/idp/google" + "github.com/gravitl/netmaker/pro/idp/okta" proLogic "github.com/gravitl/netmaker/pro/logic" "strings" "sync" @@ -72,6 +73,11 @@ func SyncFromIDP() error { } case "azure-ad": idpClient = azure.NewAzureEntraIDClient() + case "okta": + idpClient, err = okta.NewOktaClientFromSettings() + if err != nil { + return err + } default: if settings.AuthProvider != "" { return fmt.Errorf("invalid auth provider: %s", settings.AuthProvider) diff --git a/pro/auth/templates.go b/pro/auth/templates.go index 10bee9ad..6c83a7bf 100644 --- a/pro/auth/templates.go +++ b/pro/auth/templates.go @@ -115,10 +115,10 @@ var ssoErrCallbackTemplate = template.Must( id="logo" >

Server SSO Error

-

Error reason: {.Verb}

+

Error reason: {{.Verb}}

Your Netmaker server may not have SSO configured properly. - Please visit the docs for more information. + Please visit the docs for more information.

If you feel this is a mistake, please contact your network administrator. diff --git a/pro/idp/okta/okta.go b/pro/idp/okta/okta.go new file mode 100644 index 00000000..497a6e03 --- /dev/null +++ b/pro/idp/okta/okta.go @@ -0,0 +1,124 @@ +package okta + +import ( + "context" + "fmt" + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/pro/idp" + "github.com/okta/okta-sdk-golang/v5/okta" +) + +type Client struct { + client *okta.APIClient +} + +func NewOktaClient(oktaOrgURL, oktaAPIToken string) (*Client, error) { + config, err := okta.NewConfiguration( + okta.WithOrgUrl(oktaOrgURL), + okta.WithToken(oktaAPIToken), + ) + if err != nil { + return nil, err + } + + return &Client{ + client: okta.NewAPIClient(config), + }, nil +} + +func NewOktaClientFromSettings() (*Client, error) { + settings := logic.GetServerSettings() + + return NewOktaClient(settings.OktaOrgURL, settings.OktaAPIToken) +} + +func (o *Client) Verify() error { + _, _, err := o.client.UserAPI.ListUsers(context.TODO()).Limit(1).Execute() + if err != nil { + return err + } + + _, _, err = o.client.GroupAPI.ListGroups(context.TODO()).Limit(1).Execute() + return err +} + +func (o *Client) GetUsers() ([]idp.User, error) { + var retval []idp.User + var allUsersFetched bool + + for !allUsersFetched { + users, resp, err := o.client.UserAPI.ListUsers(context.TODO()).Execute() + if err != nil { + return nil, err + } + + allUsersFetched = !resp.HasNextPage() + + for _, user := range users { + id := *user.Id + username := *user.Profile.Login + + displayName := "" + if user.Profile.FirstName.IsSet() && user.Profile.LastName.IsSet() { + displayName = fmt.Sprintf("%s %s", *user.Profile.FirstName.Get(), *user.Profile.LastName.Get()) + } + + accountDisabled := false + if *user.Status == "SUSPENDED" { + accountDisabled = true + } + + retval = append(retval, idp.User{ + ID: id, + Username: username, + DisplayName: displayName, + AccountDisabled: accountDisabled, + AccountArchived: false, + }) + } + } + + return retval, nil +} + +func (o *Client) GetGroups() ([]idp.Group, error) { + var retval []idp.Group + var allGroupsFetched bool + + for !allGroupsFetched { + groups, resp, err := o.client.GroupAPI.ListGroups(context.TODO()).Execute() + if err != nil { + return nil, err + } + + allGroupsFetched = !resp.HasNextPage() + + for _, group := range groups { + var allMembersFetched bool + id := *group.Id + name := *group.Profile.Name + + var members []string + for !allMembersFetched { + groupUsers, resp, err := o.client.GroupAPI.ListGroupUsers(context.TODO(), id).Execute() + if err != nil { + return nil, err + } + + allMembersFetched = !resp.HasNextPage() + + for _, groupUser := range groupUsers { + members = append(members, *groupUser.Id) + } + } + + retval = append(retval, idp.Group{ + ID: id, + Name: name, + Members: members, + }) + } + } + + return retval, nil +} From b972e7a969f06e465f8344a18c400224fb0f555d Mon Sep 17 00:00:00 2001 From: Abhishek K Date: Mon, 11 Aug 2025 12:17:11 +0530 Subject: [PATCH 2/4] add mutex on failover ops (#3590) --- logic/peers.go | 6 ++++++ pro/controllers/failover.go | 6 ++++++ pro/logic/failover.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/logic/peers.go b/logic/peers.go index e7eb208e..d52d519f 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -242,7 +242,13 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N if peer.EgressDetails.IsEgressGateway { AddEgressInfoToPeerByAccess(&node, &peer, eli, acls, defaultDevicePolicy.Enabled) } + if node.Mutex != nil { + node.Mutex.Lock() + } _, isFailOverPeer := node.FailOverPeers[peer.ID.String()] + if node.Mutex != nil { + node.Mutex.Unlock() + } if peer.EgressDetails.IsEgressGateway { peerKey := peerHost.PublicKey.String() if isFailOverPeer && peer.FailedOverBy.String() != node.ID.String() { diff --git a/pro/controllers/failover.go b/pro/controllers/failover.go index 2321ad62..feffa171 100644 --- a/pro/controllers/failover.go +++ b/pro/controllers/failover.go @@ -110,7 +110,13 @@ func resetFailOver(w http.ResponseWriter, r *http.Request) { for _, node := range nodes { if node.FailedOverBy != uuid.Nil { node.FailedOverBy = uuid.Nil + if node.Mutex != nil { + node.Mutex.Lock() + } node.FailOverPeers = make(map[string]struct{}) + if node.Mutex != nil { + node.Mutex.Unlock() + } logic.UpsertNode(&node) } } diff --git a/pro/logic/failover.go b/pro/logic/failover.go index 9e019b70..b7e65658 100644 --- a/pro/logic/failover.go +++ b/pro/logic/failover.go @@ -51,8 +51,20 @@ func CheckFailOverCtx(failOverNode, victimNode, peerNode models.Node) error { if victimNode.FailOverPeers == nil { return nil } + if peerNode.Mutex != nil { + peerNode.Mutex.Lock() + } _, peerHasFailovered := peerNode.FailOverPeers[victimNode.ID.String()] + if peerNode.Mutex != nil { + peerNode.Mutex.Unlock() + } + if victimNode.Mutex != nil { + victimNode.Mutex.Lock() + } _, victimHasFailovered := victimNode.FailOverPeers[peerNode.ID.String()] + if victimNode.Mutex != nil { + victimNode.Mutex.Unlock() + } if peerHasFailovered && victimHasFailovered && victimNode.FailedOverBy == failOverNode.ID && peerNode.FailedOverBy == failOverNode.ID { return errors.New("failover ctx is already set") @@ -68,14 +80,38 @@ func SetFailOverCtx(failOverNode, victimNode, peerNode models.Node) error { if victimNode.FailOverPeers == nil { victimNode.FailOverPeers = make(map[string]struct{}) } + if peerNode.Mutex != nil { + peerNode.Mutex.Lock() + } _, peerHasFailovered := peerNode.FailOverPeers[victimNode.ID.String()] + if peerNode.Mutex != nil { + peerNode.Mutex.Unlock() + } + if victimNode.Mutex != nil { + victimNode.Mutex.Lock() + } _, victimHasFailovered := victimNode.FailOverPeers[peerNode.ID.String()] + if victimNode.Mutex != nil { + victimNode.Mutex.Unlock() + } if peerHasFailovered && victimHasFailovered && victimNode.FailedOverBy == failOverNode.ID && peerNode.FailedOverBy == failOverNode.ID { return errors.New("failover ctx is already set") } + if peerNode.Mutex != nil { + peerNode.Mutex.Lock() + } peerNode.FailOverPeers[victimNode.ID.String()] = struct{}{} + if peerNode.Mutex != nil { + peerNode.Mutex.Unlock() + } + if victimNode.Mutex != nil { + victimNode.Mutex.Lock() + } victimNode.FailOverPeers[peerNode.ID.String()] = struct{}{} + if victimNode.Mutex != nil { + victimNode.Mutex.Unlock() + } victimNode.FailedOverBy = failOverNode.ID peerNode.FailedOverBy = failOverNode.ID if err := logic.UpsertNode(&victimNode); err != nil { From 32657dde821575568451421fa86d2bc17000c3d6 Mon Sep 17 00:00:00 2001 From: Abhishek K Date: Mon, 11 Aug 2025 14:32:26 +0530 Subject: [PATCH 3/4] NM-9: User All resources Policy and relayed node acl Fix (#3592) * user policies fix * fix user acl rules for all resources tag * handle relayed comms via gateway with active acl policies * fix static node comms to all resources --- logic/acls.go | 106 +++++++++++ logic/extpeers.go | 7 +- pro/logic/acls.go | 448 +++++++++++++++++++++++++++------------------- pro/util.go | 1 + 4 files changed, 375 insertions(+), 187 deletions(-) diff --git a/logic/acls.go b/logic/acls.go index 876f0d77..59c5cea6 100644 --- a/logic/acls.go +++ b/logic/acls.go @@ -50,6 +50,27 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { // if nodeI.StaticNode.IngressGatewayID != node.ID.String() { // continue // } + if IsNodeAllowedToCommunicateWithAllRsrcs(nodeI) { + if nodeI.Address.IP != nil { + rules = append(rules, models.FwRule{ + SrcIP: net.IPNet{ + IP: nodeI.Address.IP, + Mask: net.CIDRMask(32, 32), + }, + Allow: true, + }) + } + if nodeI.Address6.IP != nil { + rules = append(rules, models.FwRule{ + SrcIP: net.IPNet{ + IP: nodeI.Address6.IP, + Mask: net.CIDRMask(128, 128), + }, + Allow: true, + }) + } + continue + } for _, peer := range nodes { if peer.StaticNode.ClientID == nodeI.StaticNode.ClientID || peer.IsUserNode { continue @@ -74,6 +95,37 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { } } } + if len(node.RelayedNodes) > 0 { + for _, relayedNodeID := range node.RelayedNodes { + relayedNode, err := GetNodeByID(relayedNodeID) + if err != nil { + continue + } + + if relayedNode.Address.IP != nil { + relayedFwRule := models.FwRule{ + AllowedProtocol: models.ALL, + AllowedPorts: []string{}, + Allow: true, + } + relayedFwRule.DstIP = relayedNode.AddressIPNet4() + relayedFwRule.SrcIP = node.NetworkRange + rules = append(rules, relayedFwRule) + } + + if relayedNode.Address6.IP != nil { + relayedFwRule := models.FwRule{ + AllowedProtocol: models.ALL, + AllowedPorts: []string{}, + Allow: true, + } + relayedFwRule.DstIP = relayedNode.AddressIPNet6() + relayedFwRule.SrcIP = node.NetworkRange6 + rules = append(rules, relayedFwRule) + } + + } + } return } @@ -851,6 +903,60 @@ func MigrateAclPolicies() { } +func IsNodeAllowedToCommunicateWithAllRsrcs(node models.Node) bool { + // check default policy if all allowed return true + defaultPolicy, err := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy) + if err == nil { + if defaultPolicy.Enabled { + return true + } + } + var nodeId string + if node.IsStatic { + nodeId = node.StaticNode.ClientID + node = node.StaticNode.ConvertToStaticNode() + } else { + nodeId = node.ID.String() + } + nodeTags := make(map[models.TagID]struct{}) + + nodeTags[models.TagID(nodeId)] = struct{}{} + if node.IsGw { + nodeTags[models.TagID(fmt.Sprintf("%s.%s", node.Network, models.GwTagName))] = struct{}{} + } + // list device policies + policies := ListDevicePolicies(models.NetworkID(node.Network)) + srcMap := make(map[string]struct{}) + dstMap := make(map[string]struct{}) + defer func() { + srcMap = nil + dstMap = nil + }() + for _, policy := range policies { + if !policy.Enabled { + continue + } + srcMap = ConvAclTagToValueMap(policy.Src) + dstMap = ConvAclTagToValueMap(policy.Dst) + _, srcAll := srcMap["*"] + _, dstAll := dstMap["*"] + + for tagID := range nodeTags { + if srcAll { + if _, ok := dstMap[tagID.String()]; ok { + return true + } + } + if dstAll { + if _, ok := srcMap[tagID.String()]; ok { + return true + } + } + } + } + return false +} + // IsNodeAllowedToCommunicate - check node is allowed to communicate with the peer // ADD ALLOWED DIRECTION - 0 => node -> peer, 1 => peer-> node, func isNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool) (bool, []models.Acl) { var nodeId, peerId string diff --git a/logic/extpeers.go b/logic/extpeers.go index 8704caff..3fa431df 100644 --- a/logic/extpeers.go +++ b/logic/extpeers.go @@ -729,12 +729,7 @@ func GetStaticNodesByNetwork(network models.NetworkID, onlyWg bool) (staticNode if onlyWg && extI.RemoteAccessClientID != "" { continue } - n := models.Node{ - IsStatic: true, - StaticNode: extI, - IsUserNode: extI.RemoteAccessClientID != "", - } - staticNode = append(staticNode, n) + staticNode = append(staticNode, extI.ConvertToStaticNode()) } } diff --git a/pro/logic/acls.go b/pro/logic/acls.go index 24eeaf12..19354484 100644 --- a/pro/logic/acls.go +++ b/pro/logic/acls.go @@ -17,6 +17,27 @@ func GetFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules [] defaultUserPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy) userNodes := logic.GetStaticUserNodesByNetwork(models.NetworkID(node.Network)) for _, userNodeI := range userNodes { + if defaultUserPolicy.Enabled { + if userNodeI.StaticNode.Address != "" { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet4(), + DstIP: net.IPNet{}, + AllowedProtocol: models.ALL, + AllowedPorts: []string{}, + Allow: true, + }) + } + if userNodeI.StaticNode.Address6 != "" { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet6(), + DstIP: net.IPNet{}, + AllowedProtocol: models.ALL, + AllowedPorts: []string{}, + Allow: true, + }) + } + continue + } for _, peer := range nodes { if peer.IsUserNode { continue @@ -26,68 +47,76 @@ func GetFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules [] if peer.IsStatic { peer = peer.StaticNode.ConvertToStaticNode() } - if !defaultUserPolicy.Enabled { - for _, policy := range allowedPolicies { - if userNodeI.StaticNode.Address != "" { + for _, policy := range allowedPolicies { + if userNodeI.StaticNode.Address != "" { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet4(), + DstIP: net.IPNet{ + IP: peer.Address.IP, + Mask: net.CIDRMask(32, 32), + }, + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } + if userNodeI.StaticNode.Address6 != "" { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet6(), + DstIP: net.IPNet{ + IP: peer.Address6.IP, + Mask: net.CIDRMask(128, 128), + }, + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } + + // add egress ranges + for _, dstI := range policy.Dst { + if dstI.Value == "*" { rules = append(rules, models.FwRule{ - SrcIP: userNodeI.StaticNode.AddressIPNet4(), - DstIP: net.IPNet{ - IP: peer.Address.IP, - Mask: net.CIDRMask(32, 32), - }, - AllowedProtocol: policy.Proto, - AllowedPorts: policy.Port, - Allow: true, - }) - } - if userNodeI.StaticNode.Address6 != "" { - rules = append(rules, models.FwRule{ - SrcIP: userNodeI.StaticNode.AddressIPNet6(), - DstIP: net.IPNet{ - IP: peer.Address6.IP, - Mask: net.CIDRMask(128, 128), - }, + SrcIP: userNodeI.StaticNode.AddressIPNet4(), + DstIP: net.IPNet{}, AllowedProtocol: policy.Proto, AllowedPorts: policy.Port, Allow: true, }) + break } + if dstI.ID == models.EgressID { - // add egress ranges - for _, dstI := range policy.Dst { - if dstI.ID == models.EgressID { + e := schema.Egress{ID: dstI.Value} + err := e.Get(db.WithContext(context.TODO())) + if err != nil { + continue + } + dstI.Value = e.Range - e := schema.Egress{ID: dstI.Value} - err := e.Get(db.WithContext(context.TODO())) - if err != nil { - continue - } - dstI.Value = e.Range - - ip, cidr, err := net.ParseCIDR(dstI.Value) - if err == nil { - if ip.To4() != nil && userNodeI.StaticNode.Address != "" { - rules = append(rules, models.FwRule{ - SrcIP: userNodeI.StaticNode.AddressIPNet4(), - DstIP: *cidr, - AllowedProtocol: policy.Proto, - AllowedPorts: policy.Port, - Allow: true, - }) - } else if ip.To16() != nil && userNodeI.StaticNode.Address6 != "" { - rules = append(rules, models.FwRule{ - SrcIP: userNodeI.StaticNode.AddressIPNet6(), - DstIP: *cidr, - AllowedProtocol: policy.Proto, - AllowedPorts: policy.Port, - Allow: true, - }) - } + ip, cidr, err := net.ParseCIDR(dstI.Value) + if err == nil { + if ip.To4() != nil && userNodeI.StaticNode.Address != "" { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet4(), + DstIP: *cidr, + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) + } else if ip.To16() != nil && userNodeI.StaticNode.Address6 != "" { + rules = append(rules, models.FwRule{ + SrcIP: userNodeI.StaticNode.AddressIPNet6(), + DstIP: *cidr, + AllowedProtocol: policy.Proto, + AllowedPorts: policy.Port, + Allow: true, + }) } } } - } + } } @@ -922,6 +951,8 @@ func getEgressUserRulesForNode(targetnode *models.Node, if len(egs) == 0 { return rules } + defaultPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(targetnode.Network), models.UserPolicy) + for _, egI := range egs { if !egI.Status { continue @@ -931,74 +962,69 @@ func getEgressUserRulesForNode(targetnode *models.Node, targetNodeTags[models.TagID(egI.ID)] = struct{}{} } } - for _, acl := range acls { - if !acl.Enabled { - continue - } - dstTags := logic.ConvAclTagToValueMap(acl.Dst) - for _, dst := range acl.Dst { - if dst.ID == models.EgressID { - e := schema.Egress{ID: dst.Value} - err := e.Get(db.WithContext(context.TODO())) - if err == nil && e.Status { - for nodeID := range e.Nodes { - dstTags[nodeID] = struct{}{} + if !defaultPolicy.Enabled { + for _, acl := range acls { + if !acl.Enabled { + continue + } + dstTags := logic.ConvAclTagToValueMap(acl.Dst) + for _, dst := range acl.Dst { + if dst.ID == models.EgressID { + e := schema.Egress{ID: dst.Value} + err := e.Get(db.WithContext(context.TODO())) + if err == nil && e.Status { + for nodeID := range e.Nodes { + dstTags[nodeID] = struct{}{} + } + dstTags[e.Range] = struct{}{} } - dstTags[e.Range] = struct{}{} } } - } - _, all := dstTags["*"] - addUsers := false - if !all { - for nodeTag := range targetNodeTags { - if _, ok := dstTags[nodeTag.String()]; ok { - addUsers = true - break + _, all := dstTags["*"] + addUsers := false + if !all { + for nodeTag := range targetNodeTags { + if _, ok := dstTags[nodeTag.String()]; ok { + addUsers = true + break + } } + } else { + addUsers = true } - } else { - addUsers = true - } - if addUsers { - // get all src tags - for _, srcAcl := range acl.Src { - if srcAcl.ID == models.UserAclID { - allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl) - } else if srcAcl.ID == models.UserGroupAclID { - // fetch all users in the group - if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok { - for userName := range usersMap { - allowedUsers[userName] = append(allowedUsers[userName], acl) + if addUsers { + // get all src tags + for _, srcAcl := range acl.Src { + if srcAcl.ID == models.UserAclID { + allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl) + } else if srcAcl.ID == models.UserGroupAclID { + // fetch all users in the group + if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok { + for userName := range usersMap { + allowedUsers[userName] = append(allowedUsers[userName], acl) + } } } } } - } + } } - for _, userNode := range userNodes { - if !userNode.StaticNode.Enabled { - continue + if defaultPolicy.Enabled { + r := models.AclRule{ + ID: defaultPolicy.ID, + AllowedProtocol: defaultPolicy.Proto, + AllowedPorts: defaultPolicy.Port, + Direction: defaultPolicy.AllowedDirection, + Allowed: true, } - acls, ok := allowedUsers[userNode.StaticNode.OwnerID] - if !ok { - continue - } - for _, acl := range acls { - - if !acl.Enabled { + for _, userNode := range userNodes { + if !userNode.StaticNode.Enabled { continue } - r := models.AclRule{ - ID: acl.ID, - AllowedProtocol: acl.Proto, - AllowedPorts: acl.Port, - Direction: acl.AllowedDirection, - Allowed: true, - } + // Get peers in the tags and add allowed rules if userNode.StaticNode.Address != "" { r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4()) @@ -1006,36 +1032,70 @@ func getEgressUserRulesForNode(targetnode *models.Node, if userNode.StaticNode.Address6 != "" { r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6()) } - for _, dstI := range acl.Dst { - if dstI.ID == models.EgressID { - e := schema.Egress{ID: dstI.Value} - err := e.Get(db.WithContext(context.TODO())) - if err != nil { - continue - } + } + rules[defaultPolicy.ID] = r + } else { + for _, userNode := range userNodes { + if !userNode.StaticNode.Enabled { + continue + } + + acls, ok := allowedUsers[userNode.StaticNode.OwnerID] + if !ok { + continue + } + for _, acl := range acls { + + if !acl.Enabled { + continue + } + r := models.AclRule{ + ID: acl.ID, + AllowedProtocol: acl.Proto, + AllowedPorts: acl.Port, + Direction: acl.AllowedDirection, + Allowed: true, + } + // Get peers in the tags and add allowed rules + if userNode.StaticNode.Address != "" { + r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4()) + } + if userNode.StaticNode.Address6 != "" { + r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6()) + } + for _, dstI := range acl.Dst { + if dstI.ID == models.EgressID { + e := schema.Egress{ID: dstI.Value} + err := e.Get(db.WithContext(context.TODO())) + if err != nil { + continue + } + + ip, cidr, err := net.ParseCIDR(e.Range) + if err == nil { + if ip.To4() != nil { + r.Dst = append(r.Dst, *cidr) + } else { + r.Dst6 = append(r.Dst6, *cidr) + } - ip, cidr, err := net.ParseCIDR(e.Range) - if err == nil { - if ip.To4() != nil { - r.Dst = append(r.Dst, *cidr) - } else { - r.Dst6 = append(r.Dst6, *cidr) } } } + if aclRule, ok := rules[acl.ID]; ok { + aclRule.IPList = append(aclRule.IPList, r.IPList...) + aclRule.IP6List = append(aclRule.IP6List, r.IP6List...) + rules[acl.ID] = aclRule + } else { + rules[acl.ID] = r + } + } - } - if aclRule, ok := rules[acl.ID]; ok { - aclRule.IPList = append(aclRule.IPList, r.IPList...) - aclRule.IP6List = append(aclRule.IP6List, r.IP6List...) - rules[acl.ID] = aclRule - } else { - rules[acl.ID] = r - } } } + return rules } @@ -1056,63 +1116,58 @@ func getUserAclRulesForNode(targetnode *models.Node, if targetNodeTags == nil { targetNodeTags = make(map[models.TagID]struct{}) } + defaultPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(targetnode.Network), models.UserPolicy) targetNodeTags[models.TagID(targetnode.ID.String())] = struct{}{} - for _, acl := range acls { - if !acl.Enabled { - continue - } - dstTags := logic.ConvAclTagToValueMap(acl.Dst) - _, all := dstTags["*"] - addUsers := false - if !all { - for nodeTag := range targetNodeTags { - if _, ok := dstTags[nodeTag.String()]; ok { - addUsers = true - break - } + if !defaultPolicy.Enabled { + for _, acl := range acls { + if !acl.Enabled { + continue + } + dstTags := logic.ConvAclTagToValueMap(acl.Dst) + _, all := dstTags["*"] + addUsers := false + if !all { + for nodeTag := range targetNodeTags { + if _, ok := dstTags[nodeTag.String()]; ok { + addUsers = true + break + } + } + } else { + addUsers = true } - } else { - addUsers = true - } - if addUsers { - // get all src tags - for _, srcAcl := range acl.Src { - if srcAcl.ID == models.UserAclID { - allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl) - } else if srcAcl.ID == models.UserGroupAclID { - // fetch all users in the group - if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok { - for userName := range usersMap { - allowedUsers[userName] = append(allowedUsers[userName], acl) + if addUsers { + // get all src tags + for _, srcAcl := range acl.Src { + if srcAcl.ID == models.UserAclID { + allowedUsers[srcAcl.Value] = append(allowedUsers[srcAcl.Value], acl) + } else if srcAcl.ID == models.UserGroupAclID { + // fetch all users in the group + if usersMap, ok := userGrpMap[models.UserGroupID(srcAcl.Value)]; ok { + for userName := range usersMap { + allowedUsers[userName] = append(allowedUsers[userName], acl) + } } } } } - } + } } - - for _, userNode := range userNodes { - if !userNode.StaticNode.Enabled { - continue + if defaultPolicy.Enabled { + r := models.AclRule{ + ID: defaultPolicy.ID, + AllowedProtocol: defaultPolicy.Proto, + AllowedPorts: defaultPolicy.Port, + Direction: defaultPolicy.AllowedDirection, + Allowed: true, } - acls, ok := allowedUsers[userNode.StaticNode.OwnerID] - if !ok { - continue - } - for _, acl := range acls { - - if !acl.Enabled { + for _, userNode := range userNodes { + if !userNode.StaticNode.Enabled { continue } - r := models.AclRule{ - ID: acl.ID, - AllowedProtocol: acl.Proto, - AllowedPorts: acl.Port, - Direction: acl.AllowedDirection, - Allowed: true, - } + // Get peers in the tags and add allowed rules if userNode.StaticNode.Address != "" { r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4()) @@ -1120,16 +1175,47 @@ func getUserAclRulesForNode(targetnode *models.Node, if userNode.StaticNode.Address6 != "" { r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6()) } - if aclRule, ok := rules[acl.ID]; ok { - aclRule.IPList = append(aclRule.IPList, r.IPList...) - aclRule.IP6List = append(aclRule.IP6List, r.IP6List...) - aclRule.IPList = logic.UniqueIPNetList(aclRule.IPList) - aclRule.IP6List = logic.UniqueIPNetList(aclRule.IP6List) - rules[acl.ID] = aclRule - } else { - r.IPList = logic.UniqueIPNetList(r.IPList) - r.IP6List = logic.UniqueIPNetList(r.IP6List) - rules[acl.ID] = r + } + rules[defaultPolicy.ID] = r + } else { + for _, userNode := range userNodes { + if !userNode.StaticNode.Enabled { + continue + } + acls, ok := allowedUsers[userNode.StaticNode.OwnerID] + if !ok { + continue + } + for _, acl := range acls { + + if !acl.Enabled { + continue + } + r := models.AclRule{ + ID: acl.ID, + AllowedProtocol: acl.Proto, + AllowedPorts: acl.Port, + Direction: acl.AllowedDirection, + Allowed: true, + } + // Get peers in the tags and add allowed rules + if userNode.StaticNode.Address != "" { + r.IPList = append(r.IPList, userNode.StaticNode.AddressIPNet4()) + } + if userNode.StaticNode.Address6 != "" { + r.IP6List = append(r.IP6List, userNode.StaticNode.AddressIPNet6()) + } + if aclRule, ok := rules[acl.ID]; ok { + aclRule.IPList = append(aclRule.IPList, r.IPList...) + aclRule.IP6List = append(aclRule.IP6List, r.IP6List...) + aclRule.IPList = logic.UniqueIPNetList(aclRule.IPList) + aclRule.IP6List = logic.UniqueIPNetList(aclRule.IP6List) + rules[acl.ID] = aclRule + } else { + r.IPList = logic.UniqueIPNetList(r.IPList) + r.IP6List = logic.UniqueIPNetList(r.IP6List) + rules[acl.ID] = r + } } } } @@ -1228,9 +1314,9 @@ func CheckIfAnyPolicyisUniDirectional(targetNode models.Node, acls []models.Acl) func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRule) { targetnode := *targetnodeI defer func() { - if !targetnode.IsIngressGateway { - rules = getUserAclRulesForNode(&targetnode, rules) - } + //if !targetnode.IsIngressGateway { + rules = getUserAclRulesForNode(&targetnode, rules) + //} }() rules = make(map[string]models.AclRule) var taggedNodes map[models.TagID][]models.Node diff --git a/pro/util.go b/pro/util.go index be3b4d9f..80e6e57a 100644 --- a/pro/util.go +++ b/pro/util.go @@ -5,6 +5,7 @@ package pro import ( "encoding/base64" + "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/logic" From b3253cd17d1e85a5d487a4a0d2d6f15d17c90320 Mon Sep 17 00:00:00 2001 From: Abhishek K Date: Mon, 11 Aug 2025 22:04:39 +0530 Subject: [PATCH 4/4] NM-9: fix all rsrc static node rule (#3593) * user policies fix * fix user acl rules for all resources tag * handle relayed comms via gateway with active acl policies * fix static node comms to all resources * add all resources src rule for static node --- logic/acls.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/logic/acls.go b/logic/acls.go index 59c5cea6..a34a6058 100644 --- a/logic/acls.go +++ b/logic/acls.go @@ -59,6 +59,14 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { }, Allow: true, }) + rules = append(rules, models.FwRule{ + SrcIP: node.NetworkRange, + DstIP: net.IPNet{ + IP: nodeI.Address.IP, + Mask: net.CIDRMask(32, 32), + }, + Allow: true, + }) } if nodeI.Address6.IP != nil { rules = append(rules, models.FwRule{ @@ -68,6 +76,14 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) { }, Allow: true, }) + rules = append(rules, models.FwRule{ + SrcIP: node.NetworkRange6, + DstIP: net.IPNet{ + IP: nodeI.Address.IP, + Mask: net.CIDRMask(128, 128), + }, + Allow: true, + }) } continue }