diff options
Diffstat (limited to 'src/tailnet.c')
| -rw-r--r-- | src/tailnet.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/src/tailnet.c b/src/tailnet.c new file mode 100644 index 0000000..21a90f6 --- /dev/null +++ b/src/tailnet.c @@ -0,0 +1,173 @@ +#include "tailnet.h" +#include "hoodsgate/hoodsgate.h" +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> + +DEFINE_PROGRAM_LIST(PROG_TAILSCALE, PROG_SYSCTL, PROG_UFW, PROG_CURL, PROG_NFT) + +const char *tailnetMissingProgs[MAX_PROG_COUNT]; +static int _missingProgsCount = 0; +static char *_kernelOpts[] = {"net.ipv4.ip_forward = 1\n", + "net.ipv6.conf.all.forwarding = 1\n"}; +// filename needs to be 99-tailscale.conf +static char *_sysctlConf = "/etc/sysctl.d/100-tailscale.conf"; +static char *_sysctlConfFallback = "/etc/sysctl.conf"; +static char *_ufw_file = "/etc/default/ufw"; + +bool tailnet_is_file_executable_boolean(const char *filepath) { + return access(filepath, X_OK) == 0; +} + +bool tailnet_is_program_installed_boolean(const char *program_name) { + if (!program_name || strlen(program_name) == 0) { + return false; + } + + char *path_env = getenv("PATH"); + if (!path_env) { + return false; + } + + char path_copy[4096]; + strncpy(path_copy, path_env, sizeof(path_copy) - 1); + path_copy[sizeof(path_copy) - 1] = '\0'; + + char *dir = strtok(path_copy, ":"); + while (dir != NULL) { + char fullpath[1024]; + snprintf(fullpath, sizeof(fullpath), "%s/%s", dir, program_name); + + if (tailnet_is_file_executable_boolean(fullpath)) { + return true; + } + dir = strtok(NULL, ":"); + } + return false; +} + +bool tailnet_check_required_programs_boolean(void) { + printf("Checking required programs...\n"); + bool res = true; + for (int i = 0; i < REQUIRED_PROGS_COUNT; i++) { + if (!tailnet_is_program_installed_boolean(REQUIRED_PROGS[i])) { + int pos = _missingProgsCount; + if (pos < MAX_PROG_COUNT) { + tailnetMissingProgs[pos] = REQUIRED_PROGS[i]; + _missingProgsCount++; + } + printf(" ✗ %s (MISSING)\n", REQUIRED_PROGS[i]); + res = false; + } else { + printf(" ✓ %s\n", REQUIRED_PROGS[i]); + } + } + + if (res) { + printf("All required programs found!\n"); + } else { + printf("\nPlease install missing programs. \n"); + } + + return res; +} + +bool tailnet_advertise_exit_node_boolean() { + const char *conf_file = + (access("/etc/sysctl.d/", F_OK) == 0) ? _sysctlConf : _sysctlConfFallback; + + FILE *file = fopen(conf_file, "a"); + if (!file) { + perror("Failed to open file for appending kernel opts forwarding rules"); + return false; + } + + for (int i = 0; i < (int)(sizeof(_kernelOpts) / sizeof(_kernelOpts[0])); + i++) { + fputs(_kernelOpts[i], file); + } + + fclose(file); + printf("Wrote kernel forwarding opts to %s\n", conf_file); + + if (!(access(_ufw_file, F_OK) == 0)) { + perror("Failed to access ufw default file for file ops"); + return false; + } + file = fopen(_ufw_file, "r"); + if (!file) { + perror("Failed to open file to look for ufw foward policy"); + return false; + } + + /* Following is from a comment in /etc/default/ufw + * 'Set the default forward policy to ACCEPT, DROP or REJECT. Please note + * that if you change this you will most likely want to adjust your rules + * DEFAULT_FORWARD_POLICY="DROP"' + */ + char line[512]; + bool found = false; + while (fgets(line, sizeof(line), file)) { + if (strstr(line, "DEFAULT_FORWARD_POLICY") && strstr(line, "DROP")) { + printf("Found: %s\n", line); + found = true; + break; + } else if (strstr(line, "DEFAULT_FORWARD_POLICY") && + strstr(line, "ACCEPT")) { + printf("Found: %s\nChange your policy rule in /etc/default/ufw to " + "DEFAULT_FORWARD_POLICY='DROP'\nand adjust your ufw rules " + "accordingly, then rerun", + line); + exit(1); + } else if (strstr(line, "DEFAULT_FORWARD_POLICY") && + strstr(line, "REJECT")) { + printf("Found: %s\nChange your policy rule in /etc/default/ufw to " + "DEFAULT_FORWARD_POLICY='DROP'\nand adjust your ufw rules " + "accordingly, then rerun", + line); + exit(1); + } + } + + fclose(file); + + if (found != true) { + return false; + } + + // tailscaled exit node ivp4 addr + char *const tsGetIpv4Addr[] = {PROG_TAILSCALE, "ip", "--4", NULL}; + char *exitNodeIpv4 = + hg_run_command_w_execvp_pchar(PROG_TAILSCALE, tsGetIpv4Addr, NULL); + + if (exitNodeIpv4 == NULL || hg_validate_ipv4_boolean(exitNodeIpv4) == false) { + return false; + } + + char *flagExitNode = "--exit-node="; + strcat(flagExitNode, exitNodeIpv4); + char *const tsSetExitNode[] = {PROG_TAILSCALE, "set", flagExitNode, + "--exit-node-allow-lan-access=true", NULL}; + char *setExitNode = + hg_run_command_w_execvp_pchar(PROG_TAILSCALE, tsSetExitNode, NULL); + + // not sure if the setExitNode returns output; will have to test + if (setExitNode == NULL) { + return false; + } + + char *const tsRunExitNode[] = {PROG_TAILSCALE, "up", NULL}; + char *runExitNode = + hg_run_command_w_execvp_pchar(PROG_TAILSCALE, tsRunExitNode, NULL); + + // not sure if the runExitNode returns output; will have to test + if (runExitNode == NULL) { + return false; + } + + return true; +} |
