Skip to content

Commit eb889d6

Browse files
Update kubeconfig provider script and documentation for rbac and service account creation
Signed-off-by: Codey Jenkins <[email protected]>
1 parent 49611e9 commit eb889d6

File tree

3 files changed

+265
-26
lines changed

3 files changed

+265
-26
lines changed

examples/kubeconfig/README.md

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ The kubeconfig provider allows you to:
1414
```
1515
examples/kubeconfig/
1616
├── scripts/ # Utility scripts
17-
│ └── create-kubeconfig-secret.sh
17+
│ ├── create-kubeconfig-secret.sh
18+
│ └── rules.yaml # Default RBAC rules template
1819
└── main.go # Example operator implementation
1920
```
2021

@@ -40,35 +41,96 @@ The script will:
4041
- Use the specified service account from the remote cluster
4142
- Generate a kubeconfig using the service account's token
4243
- Store the kubeconfig in a secret in your local cluster
44+
- Automatically create RBAC resources (Role/ClusterRole and bindings) with permissions defined in `rules.yaml`
45+
46+
#### Command Line Options
4347

44-
Command line options:
4548
- `-c, --context`: Kubeconfig context to use (required)
4649
- `--name`: Name for the secret (defaults to context name)
4750
- `-n, --namespace`: Namespace to create the secret in (default: "default")
48-
- `-a, --service-account`: Service account name to use from the remote cluster (default: "default")
51+
- `-a, --service-account`: Service account name to use from the remote cluster (default: "multicluster-kubeconfig-provider")
52+
- `-t, --role-type`: Create Role or ClusterRole (`role`|`clusterrole`) (default: "clusterrole")
53+
- `-r, --rules-file`: Path to custom rules file (default: `rules.yaml` in script directory)
54+
- `--skip-create-rbac`: Skip creating RBAC resources (Role/ClusterRole and bindings)
55+
- `-h, --help`: Show help message
56+
57+
#### Examples
58+
59+
```bash
60+
# Basic usage with default settings
61+
./scripts/create-kubeconfig-secret.sh -c prod-cluster
62+
63+
# Create namespace-scoped Role instead of ClusterRole
64+
./scripts/create-kubeconfig-secret.sh -c prod-cluster -t role
65+
66+
# Use custom RBAC rules file
67+
./scripts/create-kubeconfig-secret.sh -c prod-cluster -r ./custom-rules.yaml
68+
69+
# Skip RBAC creation (manual RBAC setup)
70+
./scripts/create-kubeconfig-secret.sh -c prod-cluster --skip-create-rbac
71+
72+
# Full example with all options
73+
./scripts/create-kubeconfig-secret.sh \
74+
--name my-cluster \
75+
-n my-namespace \
76+
-c prod-cluster \
77+
-a my-service-account \
78+
-t clusterrole \
79+
-r ./my-rules.yaml
80+
```
4981

50-
### 2. Customizing RBAC Rules
82+
### 2. RBAC Configuration
5183

52-
The service account in the remote cluster must have the necessary RBAC permissions for your operator to function. Edit the RBAC templates in the `rbac/` directory to define the permissions your operator needs:
84+
The script automatically creates RBAC resources with the necessary permissions for your operator. By default, it uses the rules defined in `scripts/rules.yaml`:
5385

5486
```yaml
55-
# rbac/clusterrole.yaml
56-
apiVersion: rbac.authorization.k8s.io/v1
57-
kind: ClusterRole
58-
metadata:
59-
name: ${SECRET_NAME}-role
6087
rules:
61-
# Add permissions for your operator <--------------------------------
62-
- apiGroups: [""]
63-
resources: ["configmaps"]
64-
verbs: ["list", "get", "watch"] # watch is needed for controllers that observe resources
88+
- apiGroups: [""]
89+
resources: ["configmaps"]
90+
verbs: ["list", "get", "watch"]
6591
```
6692
67-
Important RBAC considerations:
68-
- Use `watch` verb if your controller needs to observe resource changes
69-
- Use `list` and `get` for reading resources
70-
- Use `create`, `update`, `patch`, `delete` for modifying resources
71-
- Consider using `Role` instead of `ClusterRole` if you only need namespace-scoped permissions
93+
#### Customizing RBAC Rules
94+
95+
You can customize the RBAC permissions by:
96+
97+
1. **Editing the default rules file** (`scripts/rules.yaml`):
98+
```yaml
99+
rules:
100+
- apiGroups: [""]
101+
resources: ["configmaps", "secrets", "pods"]
102+
verbs: ["list", "get", "watch", "create", "update", "patch", "delete"]
103+
- apiGroups: ["apps"]
104+
resources: ["deployments"]
105+
verbs: ["list", "get", "watch"]
106+
```
107+
108+
2. **Using a custom rules file** with the `-r` option:
109+
```bash
110+
./scripts/create-kubeconfig-secret.sh -c prod-cluster -r ./my-custom-rules.yaml
111+
```
112+
113+
3. **Choosing between Role and ClusterRole**:
114+
- Use `-t role` for namespace-scoped permissions
115+
- Use `-t clusterrole` (default) for cluster-wide permissions
116+
117+
#### RBAC Resource Creation
118+
119+
The script creates the following RBAC resources automatically:
120+
121+
- **Service Account**: If it doesn't exist, creates the specified service account
122+
- **Role/ClusterRole**: With the permissions defined in the rules file
123+
- **RoleBinding/ClusterRoleBinding**: Binds the service account to the role
124+
125+
#### Skipping RBAC Creation
126+
127+
If you prefer to manage RBAC manually, use the `--skip-create-rbac` flag:
128+
129+
```bash
130+
./scripts/create-kubeconfig-secret.sh -c prod-cluster --skip-create-rbac
131+
```
132+
133+
This will only create the kubeconfig secret without setting up any RBAC resources.
72134

73135
### 3. Implementing Your Operator
74136

@@ -99,12 +161,18 @@ Your controllers can then use the manager to access any cluster and view the res
99161
- Creates a new controller-runtime cluster
100162
- Makes the cluster available to your controllers
101163
3. Your controllers can access any cluster through the manager
102-
4. RBAC rules ensure your operator has the necessary permissions in each cluster
164+
4. RBAC Rules on the remote clusters ensure the SA operator on the cluster of the controller has the necessary permissions in the remote clusters
103165

104166
## Labels and Configuration
105167

106168
The provider uses the following labels and keys by default:
107169
- Label: `sigs.k8s.io/multicluster-runtime-kubeconfig: "true"`
108170
- Secret data key: `kubeconfig`
109171

110-
You can customize these in the provider options when creating it.
172+
You can customize these in the provider options when creating it.
173+
174+
## Prerequisites
175+
176+
- `kubectl` configured with access to both the local and remote clusters
177+
- `yq` command-line tool installed (required for RBAC rule processing)
178+
- Service account with appropriate permissions in the remote cluster (if not using automatic RBAC creation script)

examples/kubeconfig/scripts/create-kubeconfig-secret.sh

Lines changed: 167 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,18 @@ set -e
66

77
# Default values
88
NAMESPACE="default"
9-
SERVICE_ACCOUNT="default"
9+
SERVICE_ACCOUNT="multicluster-kubeconfig-provider"
1010
KUBECONFIG_CONTEXT=""
1111
SECRET_NAME=""
12+
ROLE_TYPE="clusterrole"
13+
RULES_FILE=""
14+
CREATE_RBAC="true"
15+
16+
# Check for yq
17+
if ! command -v yq &>/dev/null; then
18+
echo "ERROR: 'yq' is required but not installed. Please install yq (https://mikefarah.gitbook.io/yq/) and try again."
19+
exit 1
20+
fi
1221

1322
# Function to display usage information
1423
function show_help {
@@ -17,9 +26,130 @@ function show_help {
1726
echo " --name NAME Name for the secret (defaults to context name)"
1827
echo " -n, --namespace NS Namespace to create the secret in (default: ${NAMESPACE})"
1928
echo " -a, --service-account SA Service account name to use (default: ${SERVICE_ACCOUNT})"
29+
echo " -t, --role-type TYPE Create Role or ClusterRole (role|clusterrole) (default: clusterrole)"
30+
echo " -r, --rules-file FILE Path to rules file (default: rules.yaml in script directory)"
31+
echo " --skip-create-rbac Skip creating RBAC resources (Role/ClusterRole and bindings)"
2032
echo " -h, --help Show this help message"
2133
echo ""
22-
echo "Example: $0 -c prod-cluster"
34+
echo "Examples:"
35+
echo " $0 -c prod-cluster"
36+
echo " $0 -c prod-cluster -t role -r ./custom-rules.yaml"
37+
echo " $0 -c prod-cluster -t clusterrole"
38+
echo " $0 -c prod-cluster --skip-create-rbac"
39+
}
40+
41+
# Function to create Role or ClusterRole
42+
function create_rbac {
43+
local role_type="$1"
44+
local rules_file="$2"
45+
local role_name="$3"
46+
local namespace="$4"
47+
48+
if [ ! -f "$rules_file" ]; then
49+
echo "ERROR: Rules file not found: $rules_file"
50+
exit 1
51+
fi
52+
53+
echo "Creating ${role_type} '${role_name}'..."
54+
55+
if [ "$role_type" = "role" ]; then
56+
# Create Role
57+
ROLE_YAML=$(cat <<EOF
58+
apiVersion: rbac.authorization.k8s.io/v1
59+
kind: Role
60+
metadata:
61+
name: ${role_name}
62+
namespace: ${namespace}
63+
rules:
64+
$(yq '.rules' "$rules_file")
65+
EOF
66+
)
67+
68+
echo "$ROLE_YAML" | kubectl --context=${KUBECONFIG_CONTEXT} apply -f -
69+
70+
# Create RoleBinding
71+
ROLEBINDING_YAML=$(cat <<EOF
72+
apiVersion: rbac.authorization.k8s.io/v1
73+
kind: RoleBinding
74+
metadata:
75+
name: ${role_name}-binding
76+
namespace: ${namespace}
77+
subjects:
78+
- kind: ServiceAccount
79+
name: ${SERVICE_ACCOUNT}
80+
namespace: ${namespace}
81+
roleRef:
82+
kind: Role
83+
name: ${role_name}
84+
apiGroup: rbac.authorization.k8s.io
85+
EOF
86+
)
87+
88+
echo "$ROLEBINDING_YAML" | kubectl --context=${KUBECONFIG_CONTEXT} apply -f -
89+
90+
else
91+
# Create ClusterRole
92+
CLUSTERROLE_YAML=$(cat <<EOF
93+
apiVersion: rbac.authorization.k8s.io/v1
94+
kind: ClusterRole
95+
metadata:
96+
name: ${role_name}
97+
rules:
98+
$(yq '.rules' "$rules_file")
99+
EOF
100+
)
101+
102+
echo "$CLUSTERROLE_YAML" | kubectl --context=${KUBECONFIG_CONTEXT} apply -f -
103+
104+
# Create ClusterRoleBinding
105+
CLUSTERROLEBINDING_YAML=$(cat <<EOF
106+
apiVersion: rbac.authorization.k8s.io/v1
107+
kind: ClusterRoleBinding
108+
metadata:
109+
name: ${role_name}-binding
110+
subjects:
111+
- kind: ServiceAccount
112+
name: ${SERVICE_ACCOUNT}
113+
namespace: ${namespace}
114+
roleRef:
115+
kind: ClusterRole
116+
name: ${role_name}
117+
apiGroup: rbac.authorization.k8s.io
118+
EOF
119+
)
120+
121+
echo "$CLUSTERROLEBINDING_YAML" | kubectl --context=${KUBECONFIG_CONTEXT} apply -f -
122+
fi
123+
124+
echo "$(tr '[:lower:]' '[:upper:]' <<< ${role_type:0:1})${role_type:1} '${role_name}' created successfully!"
125+
}
126+
127+
# Function to create service account if it doesn't exist
128+
function ensure_service_account {
129+
local namespace="$1"
130+
local service_account="$2"
131+
132+
echo "Checking if service account '${service_account}' exists in namespace '${namespace}'..."
133+
134+
# Check if service account exists
135+
if ! kubectl --context=${KUBECONFIG_CONTEXT} get serviceaccount ${service_account} -n ${namespace} &>/dev/null; then
136+
echo "Service account '${service_account}' not found in namespace '${namespace}'. Creating..."
137+
138+
# Create the service account
139+
SERVICE_ACCOUNT_YAML=$(cat <<EOF
140+
apiVersion: v1
141+
kind: ServiceAccount
142+
metadata:
143+
name: ${service_account}
144+
namespace: ${namespace}
145+
EOF
146+
)
147+
148+
echo "$SERVICE_ACCOUNT_YAML" | kubectl --context=${KUBECONFIG_CONTEXT} apply -f -
149+
echo "Service account '${service_account}' created successfully in namespace '${namespace}'"
150+
else
151+
echo "Service account '${service_account}' already exists in namespace '${namespace}'"
152+
fi
23153
}
24154

25155
# Parse command line options
@@ -42,6 +172,18 @@ while [[ $# -gt 0 ]]; do
42172
SERVICE_ACCOUNT="$2"
43173
shift 2
44174
;;
175+
-t|--role-type)
176+
ROLE_TYPE="$2"
177+
shift 2
178+
;;
179+
-r|--rules-file)
180+
RULES_FILE="$2"
181+
shift 2
182+
;;
183+
--skip-create-rbac)
184+
CREATE_RBAC="false"
185+
shift
186+
;;
45187
-h|--help)
46188
show_help
47189
exit 0
@@ -61,11 +203,28 @@ if [ -z "$KUBECONFIG_CONTEXT" ]; then
61203
exit 1
62204
fi
63205

206+
# Validate role type if specified
207+
if [ -n "$ROLE_TYPE" ] && [ "$ROLE_TYPE" != "role" ] && [ "$ROLE_TYPE" != "clusterrole" ]; then
208+
echo "ERROR: Invalid role type '$ROLE_TYPE'. Must be 'role' or 'clusterrole'"
209+
show_help
210+
exit 1
211+
fi
212+
213+
# Set default rules file if not specified
214+
if [ -z "$RULES_FILE" ]; then
215+
RULES_FILE="$(dirname "$0")/rules.yaml"
216+
fi
217+
64218
# Set secret name to context if not specified
65219
if [ -z "$SECRET_NAME" ]; then
66220
SECRET_NAME="$KUBECONFIG_CONTEXT"
67221
fi
68222

223+
# Create RBAC resources by default (unless --no-create-role is specified)
224+
if [ "$CREATE_RBAC" = "true" ]; then
225+
create_rbac "$ROLE_TYPE" "$RULES_FILE" "$SECRET_NAME" "$NAMESPACE"
226+
fi
227+
69228
# Get the cluster CA certificate from the remote cluster
70229
CLUSTER_CA=$(kubectl --context=${KUBECONFIG_CONTEXT} config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')
71230
if [ -z "$CLUSTER_CA" ]; then
@@ -80,6 +239,9 @@ if [ -z "$CLUSTER_SERVER" ]; then
80239
exit 1
81240
fi
82241

242+
# Ensure service account exists
243+
ensure_service_account "$NAMESPACE" "$SERVICE_ACCOUNT"
244+
83245
# Get the service account token from the remote cluster
84246
SA_TOKEN=$(kubectl --context=${KUBECONFIG_CONTEXT} -n ${NAMESPACE} create token ${SERVICE_ACCOUNT} --duration=8760h)
85247
if [ -z "$SA_TOKEN" ]; then
@@ -115,10 +277,10 @@ echo "$NEW_KUBECONFIG" > "$TEMP_KUBECONFIG"
115277

116278
# Verify the kubeconfig works
117279
echo "Verifying kubeconfig..."
118-
if ! kubectl --kubeconfig="$TEMP_KUBECONFIG" get pods -A &>/dev/null; then
280+
if ! kubectl --kubeconfig="$TEMP_KUBECONFIG" version &>/dev/null; then
119281
rm "$TEMP_KUBECONFIG"
120-
echo "ERROR: Failed to verify kubeconfig - unable to list pods."
121-
echo "- Ensure that the service account '${NAMESPACE}/${SERVICE_ACCOUNT}' on cluster '${KUBECONFIG_CONTEXT}' has the necessary permissions to list pods."
282+
echo "ERROR: Failed to verify kubeconfig - unable to connect to cluster."
283+
echo "- Ensure that the service account '${NAMESPACE}/${SERVICE_ACCOUNT}' on cluster '${KUBECONFIG_CONTEXT}' exists and is properly configured."
122284
echo "- You may specify a namespace using the -n flag."
123285
echo "- You may specify a service account using the -a flag."
124286
exit 1
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
rules:
2+
- apiGroups:
3+
- ""
4+
resources:
5+
- configmaps
6+
verbs:
7+
- list
8+
- get
9+
- watch

0 commit comments

Comments
 (0)