背景

跨部门访问的 db,需要开 IP 白名单,但考拉暂时不能提供固定 IP,只能提供网段,本文中工具实现了子网掩码生成表示整个子网的 IP 通配符格式,后续有类似的场景可以复用这个工具

为了实现跨部门访问数据库,必须配置 IP 白名单。运维平台目前无法提供固定的 IP 地址,只能提供一个网段范围。本工具能够根据子网掩码生成表示整个网段的 IP 通配符格式。在未来遇到类似的场景时,也可以直接复用该工具,从而提高工作效率。

MySQL 授权指南:授予用户 SELECT 权限

在管理 MySQL 数据库用户权限时,授予特定权限可以增强数据库的安全性。以下是创建用户或修改现有用户权限以仅授予 SELECT 权限的详细步骤。

1. 登录 MySQL 服务器

使用具有管理员权限的用户账号登录到 MySQL 服务器。

1
mysql -u admin_user -p

2. 创建用户并授予 SELECT 权限

创建新用户并仅授予 SELECT 权限

如果目标用户不存在,可以通过以下步骤创建一个新用户并仅赋予其对指定数据库的 SELECT 权限。如果该用户已经存在,可以直接修改其权限。

1
2
3
4
5
6
7
-- 创建新用户并授予SELECT权限
CREATE USER 'username'@'ip_address_or_range' IDENTIFIED BY 'password';
GRANT SELECT ON database_name.* TO 'username'@'ip_address_or_range';

-- 如果用户已存在,先撤销现有权限,再授予SELECT权限
REVOKE ALL PRIVILEGES ON database_name.* FROM 'username'@'ip_address_or_range';
GRANT SELECT ON database_name.* TO 'username'@'ip_address_or_range';

其中,ip_address_or_range 可以是一个具体的 IP 地址(例如 192.168.1.100)或者一个 IP 范围(例如 192.168.1.%)。database_name 则是你需要授权访问的数据库名称。

示例

假设你有一个数据库名为 my_database,并希望创建一个仅能读取该数据库的新用户 read_user,允许其从任意 IP 地址登录,密码为 userpassword,则命令如下:

1
2
CREATE USER 'read_user'@'%' IDENTIFIED BY 'userpassword';
GRANT SELECT ON my_database.* TO 'read_user'@'%';

3. 应用权限更改

在执行权限更改后,需要运行 FLUSH PRIVILEGES 命令以确保更改立即生效。

1
FLUSH PRIVILEGES;

Go 根据 CIDR 获取 MySQL 所需 IP 白名单配置代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package main

import (
"fmt"
"net"
"strings"
)

// TestGetIps 获取网段开始IP、结束IP、MySQL所需的白名单IP配置
func main() {
cidrs := []string{
"9.38.204.0/22",
"30.142.60.0/21",
"30.66.140.0/22",
}
var whitelistIps []string
for _, cidr := range cidrs {
startIP, endIP, err := GetIps(cidr)
if err != nil {
fmt.Printf("GetIps(%s) failed, err:%v\n", cidr, err)
}
wildcards := ipRangeToWildcard(startIP.String(), endIP.String())
fmt.Printf("cidr: %s, IP Range for: %s - %s, Wildcards: %v\n", cidr, startIP, endIP, "["+strings.Join(wildcards, ", ")+"]")
whitelistIps = append(whitelistIps, wildcards...)
}
for _, ip := range whitelistIps {
fmt.Println(ip)
}
}

func GetIps(cidr string) (startIP, endIP net.IP, err error) {
ip, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
fmt.Println("Invalid CIDR notation:", err)
return nil, nil, err
}

startIP = ip.Mask(ipnet.Mask)
endIP = make(net.IP, len(startIP))
copy(endIP, startIP)

for i := len(endIP) - 1; i >= 0; i-- {
endIP[i] |= ^ipnet.Mask[i]
}

return startIP, endIP, nil
}

// Function to convert IP range to wildcard format
func ipRangeToWildcard(startIP, endIP string) []string {
var wildcards []string
set := make(map[string]struct{})

start := net.ParseIP(startIP).To4()
end := net.ParseIP(endIP).To4()

if start == nil || end == nil {
return wildcards
}

// Convert start and end IPs to integers
startInt := ipToInt(start)
endInt := ipToInt(end)

for i := startInt; i <= endInt; {
// Get the next subnet mask length
maskLength := getNextMaskLength(i, endInt)
// Convert the current IP to wildcard format
wildcard := intToIP(i).String()[:strings.LastIndex(intToIP(i).String(), ".")+1] + "%"
// wildcards = append(wildcards, wildcard)
set[wildcard] = struct{}{}
// Move to the next subnet
shift := uint32(32 - maskLength)
i += uint32(1) << shift
}
for k := range set {
wildcards = append(wildcards, k)
}

return wildcards
}

// Function to convert IP to integer
func ipToInt(ip net.IP) uint32 {
return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3])
}

// Function to convert integer to IP
func intToIP(ipInt uint32) net.IP {
return net.IPv4(byte(ipInt>>24), byte(ipInt>>16), byte(ipInt>>8), byte(ipInt))
}

// Function to get the next subnet mask length
func getNextMaskLength(startInt, endInt uint32) int {
maskLength := 32
for maskLength > 0 {
shift := uint32(32 - maskLength)
mask := ^uint32(0) << shift
if startInt&mask == startInt && startInt|^mask <= endInt {
break
}
maskLength--
}
return maskLength
}

代码运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cidr: 9.38.204.0/22, IP Range for: 9.38.204.0 - 9.38.207.255, Wildcards: [9.38.204.%, 9.38.205.%, 9.38.206.%, 9.38.207.%]
cidr: 30.142.60.0/21, IP Range for: 30.142.56.0 - 30.142.63.255, Wildcards: [30.142.63.%, 30.142.56.%, 30.142.57.%, 30.142.58.%, 30.142.59.%, 30.142.60.%, 30.142.61.%, 30.142.62.%]
cidr: 30.66.140.0/22, IP Range for: 30.66.140.0 - 30.66.143.255, Wildcards: [30.66.140.%, 30.66.141.%, 30.66.142.%, 30.66.143.%]
9.38.204.%
9.38.205.%
9.38.206.%
9.38.207.%
30.142.63.%
30.142.56.%
30.142.57.%
30.142.58.%
30.142.59.%
30.142.60.%
30.142.61.%
30.142.62.%
30.66.140.%
30.66.141.%
30.66.142.%
30.66.143.%